diff --git a/README.md b/README.md index bde0402366e5262b8123212440cede38a9b1c5b7..bf9b9bdc016969705b570d65cff58c088bf63dce 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ ### 新一代 Spring Boot+Spring Security+Jwt 2.0来袭 ☟☟☟ -> 新一代基于Spring Boot、Spring Security、Oauth2等实现的权限控制和认证服务、支持第三方oauth授权和获取资源信息功能等、详情请点击下面的项目地址查看,欢迎大家使用体验,觉得不错的给个star,谢谢! +新一代基于Spring Boot、Spring Security、Oauth2等实现的权限控制和认证服务、支持第三方oauth授权和获取资源信息功能等、详情请点击下面的项目地址查看,欢迎大家使用体验,觉得不错的给个star,谢谢! +项目地址:https://gitee.com/micai-code/micai-platform-auth -> 项目地址:https://gitee.com/micai-code/micai-platform-auth - -### RestApi接口增加JWT认证功能 +## RestApi接口增加JWT认证功能 用户填入用户名密码后,与数据库里存储的用户信息进行比对,如果通过,则认证成功。传统的方法是在认证通过后,创建sesstion,并给客户端返回cookie。 现在我们采用JWT来处理用户名密码的认证。区别在于,认证通过后,服务器生成一个token,将token返回给客户端,客户端以后的所有请求都需要在http头中指定该token。 @@ -76,9 +75,6 @@ curl -H "Content-Type: application/json"
#### 5.设置了1分钟后Token过期,如果1分钟后再次请求/users/userList接口返回Token过期的异常提示如下图:
![输入图片说明](https://gitee.com/uploads/images/2018/0411/231525_74189dfe_130820.png "Token过期.png") -### 建议及改进
-若您有任何建议,可以通过发送邮件至827358369@qq.com向我反馈。本人承诺,任何
-建议都将会被认真考虑,优秀的建议将会被采用,但不保证一定会在当前版本中实现。
### 集成Swagger-ui,方便前后端分离开发,默认访问地址:http://localhost:8080/swagger-ui.html
@@ -89,7 +85,7 @@ curl -H "Content-Type: application/json"
### 加入微信交流群
-添加群主微信拉你进群,备注进群(免费哈) dlzhaoxinguo
+添加微信拉你进群,备注进群(免费哈) dlzhaoxinguo
### 加入讨论群
微信群: @@ -98,19 +94,8 @@ curl -H "Content-Type: application/json"
QQ群: image-20221227165831819 -### 增加了刷新token的机制 -![输入图片说明](https://images.gitee.com/uploads/images/2020/0709/141509_1d94c9cf_130820.png "001.png") -![输入图片说明](https://images.gitee.com/uploads/images/2020/0709/141544_94794671_130820.png "002.png") - ### 更新日志 -#### 2021-11-19 -1、升级spring-boot版本为最新版本,当前版本为:2.6.0-RC1
-2、暂时注释掉swagger的集成,后续再考虑 -#### 2021-11-29 -1、增加了过滤器返回自定义异常
-2、去掉了redis的集成,更加轻量级
-3、集成了hutool工具包 - +见wiki diff --git a/doc/sql/image-20221227165801242.png b/doc/sql/image-20221227165801242.png index 966fcc040e6f13bcf1d7f09c5b76b69bf09b9d66..0196a335799f1baa8b1ef1f01fef929f9bc3d70a 100644 Binary files a/doc/sql/image-20221227165801242.png and b/doc/sql/image-20221227165801242.png differ diff --git a/pom.xml b/pom.xml index 5878fbc356ff7f48ab195597d90e7eb1c64a2d5f..1215de2d1cd3271067d12dd500e7d6818407ce8d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.0-RC1 + 2.7.8 boss.portal.web diff --git a/src/main/java/boss/portal/JwtAuthApplication.java b/src/main/java/boss/portal/JwtAuthApplication.java index 9af8ec69b8ea324a282c8be6420c8d4e13d89660..bcc6c269e8df236653d6cbdd74068c15614f3a53 100644 --- a/src/main/java/boss/portal/JwtAuthApplication.java +++ b/src/main/java/boss/portal/JwtAuthApplication.java @@ -3,6 +3,11 @@ package boss.portal; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * 启动类 + * + * @author zhaoxinguo on 2017/9/12. + */ @SpringBootApplication public class JwtAuthApplication { diff --git a/src/main/java/boss/portal/config/SwaggerConfig.java b/src/main/java/boss/portal/config/SwaggerConfig.java index cea9bacf973217b58a929f4db9a95acf3a97a7e6..0525616578ee53014e18acf8a055a09341c21733 100644 --- a/src/main/java/boss/portal/config/SwaggerConfig.java +++ b/src/main/java/boss/portal/config/SwaggerConfig.java @@ -25,7 +25,7 @@ import static cn.hutool.core.collection.CollUtil.newArrayList; /** * 描述: *

- * Author: 赵新国 + * Author: zhaoxinguo * Date: 2017/12/12 15:32 */ @Configuration diff --git a/src/main/java/boss/portal/security/WebSecurityConfig.java b/src/main/java/boss/portal/config/WebSecurityConfig.java similarity index 84% rename from src/main/java/boss/portal/security/WebSecurityConfig.java rename to src/main/java/boss/portal/config/WebSecurityConfig.java index 86eca0fb8924e89f18dda5af4fcf7934d4fd7f20..7e2051e6fb58345e3d7712606233e4eabbf7304c 100644 --- a/src/main/java/boss/portal/security/WebSecurityConfig.java +++ b/src/main/java/boss/portal/config/WebSecurityConfig.java @@ -1,5 +1,6 @@ -package boss.portal.security; +package boss.portal.config; +import boss.portal.constant.AuthWhiteList; import boss.portal.filter.JWTAuthenticationFilter; import boss.portal.filter.JWTLoginFilter; import boss.portal.handler.Http401AuthenticationEntryPoint; @@ -27,24 +28,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @EnableGlobalMethodSecurity(securedEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - /** - * 需要放行的URL - */ - private static final String[] AUTH_WHITELIST = { - // -- register url - "/users/signup", - "/users/addTask", - // -- swagger ui - "/v2/api-docs", - "/swagger-resources", - "/swagger-resources/**", - "/configuration/ui", - "/configuration/security", - "/swagger-ui.html", - "/webjars/**" - // other public endpoints of your API may be appended to this array - }; - @Autowired private UserDetailsService userDetailsService; @@ -57,7 +40,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { LogoutConfigurer httpSecurityLogoutConfigurer = http.cors().and().csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() - .antMatchers(AUTH_WHITELIST).permitAll() + .antMatchers(AuthWhiteList.AUTH_WHITELIST).permitAll() .anyRequest().authenticated() // 所有请求需要身份认证 .and() .exceptionHandling() diff --git a/src/main/java/boss/portal/constant/AuthWhiteList.java b/src/main/java/boss/portal/constant/AuthWhiteList.java new file mode 100644 index 0000000000000000000000000000000000000000..69d5d0ea72fe5b55db069c56ab63a1237fe56564 --- /dev/null +++ b/src/main/java/boss/portal/constant/AuthWhiteList.java @@ -0,0 +1,26 @@ +package boss.portal.constant; + +/** + * @author zhaoxg on 2023年03月14日 10:46 + */ +public class AuthWhiteList { + + /** + * 需要放行的URL + */ + public static final String[] AUTH_WHITELIST = { + // -- register url + "/users/signup", + "/users/addTask", + "/users/userListV2", + // -- swagger ui + "/v2/api-docs", + "/swagger-resources", + "/swagger-resources/**", + "/configuration/ui", + "/configuration/security", + "/swagger-ui.html", + "/webjars/**" + // other public endpoints of your API may be appended to this array + }; +} diff --git a/src/main/java/boss/portal/constant/ConstantKey.java b/src/main/java/boss/portal/constant/ConstantKey.java index 7227a96eca5d21f431182fdbd0c4014da2802511..fbea931cb435bdea630c2bdeef02cc65fc553f23 100644 --- a/src/main/java/boss/portal/constant/ConstantKey.java +++ b/src/main/java/boss/portal/constant/ConstantKey.java @@ -1,26 +1,26 @@ -package boss.portal.constant; - -/** - * 描述: - *

- * - * @author: 赵新国 - * @date: 2018/3/12 16:07 - */ -public class ConstantKey { - - /** - * 签名key - */ - public static final String SIGNING_KEY = "spring-security-@Jwt!&Secret^#"; - - /** - * 持票人 - */ - public static final String BEARER = "Bearer "; - - /** - * 在头部标签中存放Token的key - */ - public static final String HEADER_KEY = "Authorization"; +package boss.portal.constant; + +/** + * 描述: + *

+ * + * @author: zhaoxinguo + * @date: 2018/3/12 16:07 + */ +public class ConstantKey { + + /** + * 签名key + */ + public static final String SIGNING_KEY = "spring-security-@Jwt!&Secret^#"; + + /** + * 持票人 + */ + public static final String BEARER = "Bearer "; + + /** + * 在头部标签中存放Token的key + */ + public static final String HEADER_KEY = "Authorization"; } \ No newline at end of file diff --git a/src/main/java/boss/portal/controller/BaseController.java b/src/main/java/boss/portal/controller/BaseController.java index 967545f3d7f7f3b7c0a7925609732aa8c10295be..b74e70a6ded6cdb369219ce4fdb4c3d13bbc616e 100644 --- a/src/main/java/boss/portal/controller/BaseController.java +++ b/src/main/java/boss/portal/controller/BaseController.java @@ -1,47 +1,47 @@ -package boss.portal.controller; - -import boss.portal.repository.UserRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * 描述: - *

- * - * @author: 赵新国 - * @date: 2018/6/5 18:35 - */ -public abstract class BaseController { - - protected Logger logger = LoggerFactory.getLogger(BaseController.class); - - @Autowired - protected UserRepository userRepository; - - @Autowired - protected BCryptPasswordEncoder bCryptPasswordEncoder; - - /** - * 获取用户所拥有的权限列表 - * @return - */ - public List getAuthentication() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - Collection authorities = authentication.getAuthorities(); - List list = new ArrayList<>(); - for (GrantedAuthority grantedAuthority : authorities) { - logger.info("权限列表:{}", grantedAuthority.getAuthority()); - list.add(grantedAuthority.getAuthority()); - } - return list; - } -} +package boss.portal.controller; + +import boss.portal.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * 描述: + *

+ * + * @author: zhaoxinguo + * @date: 2018/6/5 18:35 + */ +public abstract class BaseController { + + protected Logger logger = LoggerFactory.getLogger(BaseController.class); + + @Autowired + protected UserRepository userRepository; + + @Autowired + protected BCryptPasswordEncoder bCryptPasswordEncoder; + + /** + * 获取用户所拥有的权限列表 + * @return + */ + public List getAuthentication() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Collection authorities = authentication.getAuthorities(); + List list = new ArrayList<>(); + for (GrantedAuthority grantedAuthority : authorities) { + logger.info("权限列表:{}", grantedAuthority.getAuthority()); + list.add(grantedAuthority.getAuthority()); + } + return list; + } +} diff --git a/src/main/java/boss/portal/controller/UserController.java b/src/main/java/boss/portal/controller/UserController.java index 391438c38814b80bf808bc40d3f104f1f9cd7457..f53a628b40e594a9cdfeae55e05beabe829987ff 100644 --- a/src/main/java/boss/portal/controller/UserController.java +++ b/src/main/java/boss/portal/controller/UserController.java @@ -2,6 +2,8 @@ package boss.portal.controller; import boss.portal.entity.User; import boss.portal.exception.UsernameIsExitedException; +import boss.portal.param.Result; +import com.alibaba.fastjson.JSON; import io.swagger.annotations.Api; import io.swagger.annotations.ApiModelProperty; import org.springframework.web.bind.annotation.*; @@ -38,12 +40,12 @@ public class UserController extends BaseController { */ @ApiModelProperty(value = "获取用户列表") @GetMapping("/userList") - public Map userList(){ + public Result userList(){ List users = userRepository.findAll(); - logger.info("users: {}", users); + logger.info("users: {}", JSON.toJSON(users)); Map map = new HashMap(); map.put("users",users); - return map; + return Result.ok(map); } /** @@ -52,9 +54,23 @@ public class UserController extends BaseController { */ @ApiModelProperty(value = "获取用户权限") @GetMapping("/authorityList") - public List authorityList(){ + public Result authorityList(){ List authentication = getAuthentication(); - return authentication; + return Result.ok(authentication); + } + + /** + * 获取用户列表V2-验证不登录就可以直接请求该接口(前端传递token的情况下) + * @return + */ + @ApiModelProperty(value = "获取用户列表V2") + @GetMapping("/userListV2") + public Result userListV2(){ + List users = userRepository.findAll(); + logger.info("users: {}", JSON.toJSON(users)); + Map map = new HashMap(); + map.put("users",users); + return Result.ok(map); } } diff --git a/src/main/java/boss/portal/exception/GlobalExceptionHandler.java b/src/main/java/boss/portal/exception/GlobalExceptionHandler.java index 945d15f4c4d698e93b83ae7fdf66919425dfa4b3..dd57584f3224c3f8fae771e5ef08935085e37e50 100644 --- a/src/main/java/boss/portal/exception/GlobalExceptionHandler.java +++ b/src/main/java/boss/portal/exception/GlobalExceptionHandler.java @@ -1,6 +1,6 @@ package boss.portal.exception; -import boss.portal.result.Result; +import boss.portal.param.Result; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureException; diff --git a/src/main/java/boss/portal/exception/ServiceException.java b/src/main/java/boss/portal/exception/ServiceException.java index 86c5b499e7434390a3de68d65d486d6b06681594..eda8b5d5112be9537f105ec553b416d22a477037 100644 --- a/src/main/java/boss/portal/exception/ServiceException.java +++ b/src/main/java/boss/portal/exception/ServiceException.java @@ -4,7 +4,7 @@ package boss.portal.exception; * 描述: *

* - * @author: 赵新国 + * @author: zhaoxinguo * @date: 2018/4/11 23:06 */ public class ServiceException extends RuntimeException { diff --git a/src/main/java/boss/portal/filter/JWTAuthenticationFilter.java b/src/main/java/boss/portal/filter/JWTAuthenticationFilter.java index ca4cf9b56546ebc6418bdcd882b8e2a2fd07dbba..711dd9f590e6e7e77b38a849fd2ece96bdcc9d9d 100644 --- a/src/main/java/boss/portal/filter/JWTAuthenticationFilter.java +++ b/src/main/java/boss/portal/filter/JWTAuthenticationFilter.java @@ -1,5 +1,6 @@ package boss.portal.filter; +import boss.portal.constant.AuthWhiteList; import boss.portal.constant.ConstantKey; import boss.portal.exception.ServiceException; import boss.portal.service.impl.GrantedAuthorityImpl; @@ -18,9 +19,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; +import java.util.*; /** * 自定义JWT认证过滤器 @@ -39,11 +38,23 @@ public class JWTAuthenticationFilter extends BasicAuthenticationFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + String requestURI = request.getRequestURI(); String header = request.getHeader(ConstantKey.HEADER_KEY); if (ObjectUtil.isEmpty(header) || !header.startsWith(ConstantKey.BEARER)) { chain.doFilter(request, response); return; } + + // 如果token不为空,并且是以指定票据开头 + if (ObjectUtil.isNotEmpty(header) && header.startsWith(ConstantKey.BEARER)) { + // 如果请求路径是放行路径,则直接跳过认证 + List anonUrlList = Arrays.asList(AuthWhiteList.AUTH_WHITELIST); + if (anonUrlList.contains(requestURI)) { + chain.doFilter(request, response); + return; + } + } + UsernamePasswordAuthenticationToken authentication = getAuthentication(request, response); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); @@ -95,10 +106,18 @@ public class JWTAuthenticationFilter extends BasicAuthenticationFilter { logger.info("执行时间: {}", (end - start) + " 毫秒"); user = claims.getSubject(); if (user != null) { - String[] split = user.split("-")[1].split(","); + String[] authoritys = user.split("-")[1].split(","); ArrayList authorities = new ArrayList<>(); - for (int i=0; i < split.length; i++) { - authorities.add(new GrantedAuthorityImpl(split[i])); + for (int i = 0; i < authoritys.length; i++) { + String authority = authoritys[i]; + // 处理解析权限异常,在注入权限的时候直接用的:用户名+数组,导致字符串中是"admin-[admin,xx1,xx2]",而在解析的时候没有把两个括号[]去掉,导致权限识别错误,识别成了"[admin"和"xx2]"。 + if (i == 0) { + authority = authority.replaceAll("\\[", ""); + } + if (i == authoritys.length - 1) { + authority = authority.replaceAll("\\]", ""); + } + authorities.add(new GrantedAuthorityImpl(authority)); } return new UsernamePasswordAuthenticationToken(user, null, authorities); } diff --git a/src/main/java/boss/portal/filter/JWTLoginFilter.java b/src/main/java/boss/portal/filter/JWTLoginFilter.java index fdc2f875a3feef3c7f8a980ec069592871562111..74302bd63ac4f86d721d86419732013ca451d2cd 100644 --- a/src/main/java/boss/portal/filter/JWTLoginFilter.java +++ b/src/main/java/boss/portal/filter/JWTLoginFilter.java @@ -2,7 +2,7 @@ package boss.portal.filter; import boss.portal.constant.ConstantKey; import boss.portal.entity.User; -import boss.portal.result.Result; +import boss.portal.param.Result; import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Jwts; diff --git a/src/main/java/boss/portal/param/LoginParam.java b/src/main/java/boss/portal/param/LoginParam.java index e8e610fd83f930fcf6413141eefc320c2a21e2a4..97234099e450c3fb66790654aaee49565d6b6a4c 100644 --- a/src/main/java/boss/portal/param/LoginParam.java +++ b/src/main/java/boss/portal/param/LoginParam.java @@ -1,30 +1,30 @@ -package boss.portal.param; - -/** - * 描述: - *

- * - * @author: 赵新国 - * @date: 2018/6/4 20:48 - */ -public class LoginParam { - - private String username; - private String password; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} +package boss.portal.param; + +/** + * 描述: + *

+ * + * @author: zhaoxinguo + * @date: 2018/6/4 20:48 + */ +public class LoginParam { + + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/boss/portal/result/Result.java b/src/main/java/boss/portal/param/Result.java similarity index 97% rename from src/main/java/boss/portal/result/Result.java rename to src/main/java/boss/portal/param/Result.java index a2cb8b3c2d31bfea28a988c219cc11efc59d9398..6db6dcc9b8eaa9134799e06581bf114f3412e890 100644 --- a/src/main/java/boss/portal/result/Result.java +++ b/src/main/java/boss/portal/param/Result.java @@ -1,4 +1,4 @@ -package boss.portal.result; +package boss.portal.param; import java.util.HashMap; import java.util.Map; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d5c53d286187fbd40b5c6f41198a63fb5b09c0e0..68729ac861fc7d1c95659c8900478d5f231fe6a3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,15 +1,13 @@ +# JDBC Config spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test?prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true spring.datasource.username=root spring.datasource.password=root - +# JPA Config spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jackson.serialization.indent_output=true - -# ????Springfox??????????AntPathMatcher???Spring Boot 2.6.0????PathPatternMatcher? spring.mvc.pathmatch.matching-strategy=ant_path_matcher - # JWT Config jwt.header=Authorization jwt.secret=NDU0NTY4amhmc3NkeHp6eGNxdzIlMjFAJTIxQCUyM2ZmNQ== @@ -17,3 +15,8 @@ jwt.expiration=7200000 + + + + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000000000000000000000000000000000..bb14b82c94d4ad85c204ef9166426ffd583a63d2 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,189 @@ + + + + + + + + + + logback + + + + + + + + + + + + + + + + + + + debug + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + + + + + ${log.path}/log_debug.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + ${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + debug + ACCEPT + DENY + + + + + + + ${log.path}/log_info.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + info + ACCEPT + DENY + + + + + + + ${log.path}/log_warn.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + warn + ACCEPT + DENY + + + + + + + + ${log.path}/log_error.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + ERROR + ACCEPT + DENY + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file