From 3ff462305ba1b87c7865e59dd9ebf060bf8d3ba1 Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Wed, 13 Sep 2023 18:27:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20SpringSecurity=20+=20JWT=20?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E9=89=B4=E6=9D=83=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/aspect/DuplicateSubmitAspect.java | 18 +-- .../common/annotation/DataPermission.java | 2 - .../system/{ => common}/util/ExcelUtils.java | 2 +- .../{ => common}/util/ResponseUtils.java | 5 +- .../{ => common}/util/SecurityUtils.java | 2 +- .../youlai/system/config/SecurityConfig.java | 8 +- .../system/controller/SysUserController.java | 2 +- .../filter/JwtAuthenticationFilter.java | 63 -------- .../system/filter/VerifyCodeFilter.java | 2 +- .../mybatisplus/MyDataPermissionHandler.java | 2 +- .../interceptor/AuthChannelInterceptor.java | 20 +-- .../exception/MyAccessDeniedHandler.java | 2 +- .../system/security/jwt/JwtTokenFilter.java | 44 ++++++ .../JwtTokenProvider.java} | 140 +++++++++--------- .../security/service/PermissionService.java | 2 +- .../system/service/impl/AuthServiceImpl.java | 11 +- .../service/impl/SysRoleServiceImpl.java | 2 +- .../service/impl/SysUserServiceImpl.java | 2 +- .../com/youlai/system/util/RequestUtils.java | 24 --- src/main/resources/application-dev.yml | 10 +- src/main/resources/application-prod.yml | 11 +- 21 files changed, 160 insertions(+), 214 deletions(-) rename src/main/java/com/youlai/system/{ => common}/util/ExcelUtils.java (91%) rename src/main/java/com/youlai/system/{ => common}/util/ResponseUtils.java (89%) rename src/main/java/com/youlai/system/{ => common}/util/SecurityUtils.java (99%) delete mode 100644 src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/youlai/system/security/jwt/JwtTokenFilter.java rename src/main/java/com/youlai/system/security/{JwtTokenManager.java => jwt/JwtTokenProvider.java} (52%) delete mode 100644 src/main/java/com/youlai/system/util/RequestUtils.java diff --git a/src/main/java/com/youlai/system/aspect/DuplicateSubmitAspect.java b/src/main/java/com/youlai/system/aspect/DuplicateSubmitAspect.java index ccb746a5..5a074a82 100644 --- a/src/main/java/com/youlai/system/aspect/DuplicateSubmitAspect.java +++ b/src/main/java/com/youlai/system/aspect/DuplicateSubmitAspect.java @@ -1,11 +1,10 @@ package com.youlai.system.aspect; import cn.hutool.core.util.StrUtil; -import com.youlai.system.exception.BusinessException; -import com.youlai.system.common.result.ResultCode; -import com.youlai.system.util.RequestUtils; import com.youlai.system.common.annotation.PreventDuplicateSubmit; -import com.youlai.system.security.JwtTokenManager; +import com.youlai.system.common.result.ResultCode; +import com.youlai.system.exception.BusinessException; +import com.youlai.system.security.jwt.JwtTokenProvider; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,7 +24,7 @@ import java.util.concurrent.TimeUnit; * 处理重复提交的切面 * * @author haoxr - * @since 3.0.0 + * @since 2.3.0 */ @Aspect @Component @@ -35,7 +34,7 @@ public class DuplicateSubmitAspect { private final RedissonClient redissonClient; - private final JwtTokenManager jwtTokenManager; + private final JwtTokenProvider jwtTokenProvider; private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:"; /** @@ -68,9 +67,10 @@ public class DuplicateSubmitAspect { private String generateResubmitLockKey() { String resubmitLockKey = null; HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - String jwt = RequestUtils.resolveToken(request); - if (StrUtil.isNotBlank(jwt)) { - String jti = (String) jwtTokenManager.getTokenClaims(jwt).get("jti"); + + String token = jwtTokenProvider.resolveToken(request); + if (StrUtil.isNotBlank(token)) { + String jti = (String) jwtTokenProvider.getTokenClaims(token).get("jti"); resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI(); } return resubmitLockKey; diff --git a/src/main/java/com/youlai/system/common/annotation/DataPermission.java b/src/main/java/com/youlai/system/common/annotation/DataPermission.java index fb716d07..50789b5c 100644 --- a/src/main/java/com/youlai/system/common/annotation/DataPermission.java +++ b/src/main/java/com/youlai/system/common/annotation/DataPermission.java @@ -4,10 +4,8 @@ import java.lang.annotation.*; /** * MP数据权限注解 - *

* * @author zc - * @link https://gitee.com/baomidou/mybatis-plus/issues/I37I90 * @since 2.0.0 */ @Documented diff --git a/src/main/java/com/youlai/system/util/ExcelUtils.java b/src/main/java/com/youlai/system/common/util/ExcelUtils.java similarity index 91% rename from src/main/java/com/youlai/system/util/ExcelUtils.java rename to src/main/java/com/youlai/system/common/util/ExcelUtils.java index ae0ac6d6..aa766ea1 100644 --- a/src/main/java/com/youlai/system/util/ExcelUtils.java +++ b/src/main/java/com/youlai/system/common/util/ExcelUtils.java @@ -1,4 +1,4 @@ -package com.youlai.system.util; +package com.youlai.system.common.util; import com.alibaba.excel.EasyExcel; import com.youlai.system.listener.easyexcel.MyAnalysisEventListener; diff --git a/src/main/java/com/youlai/system/util/ResponseUtils.java b/src/main/java/com/youlai/system/common/util/ResponseUtils.java similarity index 89% rename from src/main/java/com/youlai/system/util/ResponseUtils.java rename to src/main/java/com/youlai/system/common/util/ResponseUtils.java index 4b6a17d3..2ed64175 100644 --- a/src/main/java/com/youlai/system/util/ResponseUtils.java +++ b/src/main/java/com/youlai/system/common/util/ResponseUtils.java @@ -1,6 +1,7 @@ -package com.youlai.system.util; +package com.youlai.system.common.util; import cn.hutool.json.JSONUtil; +import com.youlai.system.common.result.IResultCode; import com.youlai.system.common.result.Result; import com.youlai.system.common.result.ResultCode; @@ -10,6 +11,8 @@ import org.springframework.http.MediaType; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import static com.youlai.system.common.result.ResultCode.*; + /** * 响应工具类 * diff --git a/src/main/java/com/youlai/system/util/SecurityUtils.java b/src/main/java/com/youlai/system/common/util/SecurityUtils.java similarity index 99% rename from src/main/java/com/youlai/system/util/SecurityUtils.java rename to src/main/java/com/youlai/system/common/util/SecurityUtils.java index 2234f358..99d114f8 100644 --- a/src/main/java/com/youlai/system/util/SecurityUtils.java +++ b/src/main/java/com/youlai/system/common/util/SecurityUtils.java @@ -1,4 +1,4 @@ -package com.youlai.system.util; +package com.youlai.system.common.util; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; diff --git a/src/main/java/com/youlai/system/config/SecurityConfig.java b/src/main/java/com/youlai/system/config/SecurityConfig.java index 89dfe2a7..1328e24f 100644 --- a/src/main/java/com/youlai/system/config/SecurityConfig.java +++ b/src/main/java/com/youlai/system/config/SecurityConfig.java @@ -1,11 +1,11 @@ package com.youlai.system.config; import com.youlai.system.common.constant.SecurityConstants; -import com.youlai.system.filter.JwtAuthenticationFilter; import com.youlai.system.security.exception.MyAccessDeniedHandler; import com.youlai.system.security.exception.MyAuthenticationEntryPoint; -import com.youlai.system.security.JwtTokenManager; +import com.youlai.system.security.jwt.JwtTokenFilter; import com.youlai.system.filter.VerifyCodeFilter; +import com.youlai.system.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,7 +36,7 @@ public class SecurityConfig { private final MyAuthenticationEntryPoint authenticationEntryPoint; private final MyAccessDeniedHandler accessDeniedHandler; - private final JwtTokenManager jwtTokenManager; + private final JwtTokenProvider jwtTokenProvider; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -58,7 +58,7 @@ public class SecurityConfig { // 验证码校验过滤器 http.addFilterBefore(new VerifyCodeFilter(),UsernamePasswordAuthenticationFilter.class); // JWT 校验过滤器 - http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenManager), UsernamePasswordAuthenticationFilter.class); + http.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); return http.build(); } diff --git a/src/main/java/com/youlai/system/controller/SysUserController.java b/src/main/java/com/youlai/system/controller/SysUserController.java index 4b1a89bf..58f34846 100644 --- a/src/main/java/com/youlai/system/controller/SysUserController.java +++ b/src/main/java/com/youlai/system/controller/SysUserController.java @@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.youlai.system.common.constant.ExcelConstants; import com.youlai.system.common.result.PageResult; import com.youlai.system.common.result.Result; -import com.youlai.system.util.ExcelUtils; +import com.youlai.system.common.util.ExcelUtils; import com.youlai.system.common.annotation.PreventDuplicateSubmit; import com.youlai.system.listener.easyexcel.UserImportListener; import com.youlai.system.model.vo.UserImportVO; diff --git a/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java b/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java deleted file mode 100644 index 64364ae8..00000000 --- a/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.youlai.system.filter; - -import cn.hutool.core.util.StrUtil; -import com.youlai.system.common.constant.SecurityConstants; -import com.youlai.system.common.result.ResultCode; -import com.youlai.system.util.RequestUtils; -import com.youlai.system.util.ResponseUtils; -import com.youlai.system.security.JwtTokenManager; -import io.jsonwebtoken.Claims; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -/** - * JWT 校验过滤器 - * - * @author haoxr - * @since 2022/10/1 - */ -public class JwtAuthenticationFilter extends OncePerRequestFilter { - - private static final AntPathRequestMatcher LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(SecurityConstants.LOGIN_PATH, "POST"); - - private final JwtTokenManager tokenManager; - - public JwtAuthenticationFilter(JwtTokenManager tokenManager) { - this.tokenManager = tokenManager; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - // 登录接口放行是走过滤器链的方式(验证码校验过滤器),这里拦截到登录接口需要手动放行 - if (LOGIN_PATH_REQUEST_MATCHER.matches(request)) { - // 手动放行登录接口 - chain.doFilter(request, response); - }else{ - String jwt = RequestUtils.resolveToken(request); - if (StrUtil.isNotBlank(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) { - try { - // 解析&验证 JWT - Claims claims = this.tokenManager.parseAndValidateToken(jwt); - - // JWT验证有效获取Authentication存入Security上下文 - Authentication authentication = this.tokenManager.getAuthentication(claims); - SecurityContextHolder.getContext().setAuthentication(authentication); - - chain.doFilter(request, response); - }catch (Exception e){ - ResponseUtils.writeErrMsg(response, ResultCode.TOKEN_INVALID); - } - }else{ - ResponseUtils.writeErrMsg(response, ResultCode.TOKEN_INVALID); - } - } - } -} diff --git a/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java b/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java index bcae036d..3e6c5fb7 100644 --- a/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java +++ b/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java @@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.common.result.ResultCode; -import com.youlai.system.util.ResponseUtils; +import com.youlai.system.common.util.ResponseUtils; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java b/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java index aa713b79..cb22cb64 100644 --- a/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java +++ b/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java @@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; import com.youlai.system.common.annotation.DataPermission; import com.youlai.system.common.base.IBaseEnum; import com.youlai.system.common.enums.DataScopeEnum; -import com.youlai.system.util.SecurityUtils; +import com.youlai.system.common.util.SecurityUtils; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Expression; diff --git a/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java b/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java index 38bc7f7b..149f5774 100644 --- a/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java +++ b/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java @@ -1,9 +1,7 @@ package com.youlai.system.interceptor; import cn.hutool.core.util.StrUtil; -import com.youlai.system.common.constant.SecurityConstants; -import com.youlai.system.security.JwtTokenManager; -import io.jsonwebtoken.Claims; +import com.youlai.system.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; @@ -25,13 +23,13 @@ import java.security.Principal; @RequiredArgsConstructor public class AuthChannelInterceptor implements ChannelInterceptor { - private final JwtTokenManager jwtTokenManager; + private final JwtTokenProvider jwtTokenProvider; /** * 连接前监听 * - * @param message - * @param channel + * @param message 消息 + * @param channel 通道 * @return */ @Override @@ -41,14 +39,12 @@ public class AuthChannelInterceptor implements ChannelInterceptor { if (StompCommand.CONNECT.equals(accessor.getCommand())) { // get token from header - String token = accessor.getFirstNativeHeader("Authorization"); + String bearerToken = accessor.getFirstNativeHeader("Authorization"); // if token is not null - if (StrUtil.isNotBlank(token)) { + if (StrUtil.isNotBlank(bearerToken)) { - token = token.substring(SecurityConstants.TOKEN_PREFIX.length()); - Claims claims = jwtTokenManager.parseAndValidateToken(token); - - String username = claims.get("username", String.class); + bearerToken = bearerToken.substring(7); + String username = jwtTokenProvider.getUsername(bearerToken); // if the username is not null, assign it to the Principal. if (StrUtil.isNotBlank(username)) { Principal principal = () -> username; diff --git a/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java b/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java index 78b0fbc8..83515d17 100644 --- a/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java +++ b/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java @@ -1,7 +1,7 @@ package com.youlai.system.security.exception; import com.youlai.system.common.result.ResultCode; -import com.youlai.system.util.ResponseUtils; +import com.youlai.system.common.util.ResponseUtils; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/youlai/system/security/jwt/JwtTokenFilter.java b/src/main/java/com/youlai/system/security/jwt/JwtTokenFilter.java new file mode 100644 index 00000000..8d03542b --- /dev/null +++ b/src/main/java/com/youlai/system/security/jwt/JwtTokenFilter.java @@ -0,0 +1,44 @@ +package com.youlai.system.security.jwt; + +import com.youlai.system.common.result.ResultCode; +import com.youlai.system.common.util.ResponseUtils; +import com.youlai.system.exception.BusinessException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * @author haoxr + * @since 2023/9/13 + */ +public class JwtTokenFilter extends OncePerRequestFilter { + private JwtTokenProvider jwtTokenProvider; + + public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) { + this.jwtTokenProvider = jwtTokenProvider; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String token = jwtTokenProvider.resolveToken(request); + try { + if (token != null && jwtTokenProvider.validateToken(token)) { + Authentication auth = jwtTokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(auth); + } + } catch (BusinessException ex) { + //this is very important, since it guarantees the user is not authenticated at all + SecurityContextHolder.clearContext(); + ResponseUtils.writeErrMsg(response,(ResultCode)ex.getResultCode()); + return; + } + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/youlai/system/security/JwtTokenManager.java b/src/main/java/com/youlai/system/security/jwt/JwtTokenProvider.java similarity index 52% rename from src/main/java/com/youlai/system/security/JwtTokenManager.java rename to src/main/java/com/youlai/system/security/jwt/JwtTokenProvider.java index 20928dce..e5009b2f 100644 --- a/src/main/java/com/youlai/system/security/JwtTokenManager.java +++ b/src/main/java/com/youlai/system/security/jwt/JwtTokenProvider.java @@ -1,13 +1,17 @@ -package com.youlai.system.security; +package com.youlai.system.security.jwt; import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.IdUtil; import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.security.model.SysUserDetails; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.DecodingException; import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -15,62 +19,53 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; -import jakarta.annotation.Resource; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** - * JWT token 管理器 + * JWT token 管理器 * * @author haoxr - * @since 2022/10/22 + * @since 2023/9/13 */ + @Component -public class JwtTokenManager { - - /** - * token加密密钥 - */ - @Value("${auth.token.secret_key}") - private String secretKey; - - /** - * token有效期(单位:秒) - */ - @Value("${auth.token.ttl}") - private Long tokenTtl; - - /** - * secret key byte array. - */ - private byte[] secretKeyBytes; - - private JwtParser jwtParser; +public class JwtTokenProvider { @Resource private RedisTemplate redisTemplate; + @Value("${jwt.secret-key:123456}") + private String secretKey; + + @Value("${jwt.expiration:7200}") + private int expiration; + + private byte[] secretKeyBytes; + + + @PostConstruct + protected void init() { + secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); + } + /** - * Create token. + * 创建Token * - * @param authentication auth info - * @return token + * @param authentication + * @return */ public String createToken(Authentication authentication) { Claims claims = Jwts.claims().setSubject(authentication.getName()); + SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal(); - claims.put("jti",IdUtil.fastSimpleUUID()); claims.put("userId", userDetails.getUserId()); claims.put("username", claims.getSubject()); claims.put("deptId", userDetails.getDeptId()); claims.put("dataScope", userDetails.getDataScope()); - // 角色放入JWT的claims Set roles = userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); claims.put("authorities", roles); @@ -79,48 +74,57 @@ public class JwtTokenManager { Set perms = userDetails.getPerms(); redisTemplate.opsForValue().set(SecurityConstants.USER_PERMS_CACHE_PREFIX + userDetails.getUserId(), perms); - // 过期时间 - Date expirationTime = new Date(System.currentTimeMillis() + tokenTtl * 1000L); + + Date now = new Date(); + Date expirationTime = new Date(now.getTime() + expiration * 1000L); return Jwts.builder() - //.setId(IdUtil.fastSimpleUUID()) TODO 设置jti无效 .setClaims(claims) + .setIssuedAt(now) .setExpiration(expirationTime) - .signWith(Keys.hmacShaKeyFor(this.getSecretKeyBytes()), SignatureAlgorithm.HS256).compact(); + .signWith(Keys.hmacShaKeyFor(getSecretKeyBytes()), SignatureAlgorithm.HS256).compact(); } - /** - * 获取认证信息 - */ - public Authentication getAuthentication( Claims claims) { - SysUserDetails principal = new SysUserDetails(); - principal.setUserId(Convert.toLong(claims.get("userId"))); // 用户ID - principal.setUsername(Convert.toStr(claims.get("username"))); // 用户名 - principal.setDeptId(Convert.toLong(claims.get("deptId"))); // 部门ID - principal.setDataScope(Convert.toInt(claims.get("dataScope"))); // 数据权限 + public Authentication getAuthentication(String token) { + Claims claims = this.getTokenClaims(token); + + SysUserDetails userDetails = new SysUserDetails(); + userDetails.setUserId(Convert.toLong(claims.get("userId"))); // 用户ID + userDetails.setUsername(Convert.toStr(claims.get("username"))); // 用户名 + userDetails.setDeptId(Convert.toLong(claims.get("deptId"))); // 部门ID + userDetails.setDataScope(Convert.toInt(claims.get("dataScope"))); // 数据权限范围 List authorities = ((ArrayList) claims.get("authorities")) .stream() - .map(role -> new SimpleGrantedAuthority(role)) - .collect(Collectors.toList()); + .map(SimpleGrantedAuthority::new) + .toList(); - return new UsernamePasswordAuthenticationToken(principal, "", authorities); + return new UsernamePasswordAuthenticationToken(userDetails, "", authorities); } - /** - * 解析 & 验证 token - */ - public Claims parseAndValidateToken(String token) { - // 解析成功说明JWT有效 - Claims claims = this.getTokenClaims(token); - // 验证JWT 是否在黑名单(注销场景会存入黑名单) - Boolean isBlack = redisTemplate.hasKey(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + claims.get("jti")); - - if (isBlack) { - throw new RuntimeException("token 已被禁用"); + public String resolveToken(HttpServletRequest req) { + String bearerToken = req.getHeader("Authorization"); + if (bearerToken != null && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); } + return null; + } + + public boolean validateToken(String token) { + Jwts.parserBuilder().setSigningKey(getSecretKeyBytes()).build().parseClaimsJws(token); + return true; + } + + public String getUsername(String token) { + return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); + } + + + public Claims getTokenClaims(String token) { + Claims claims = Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build().parseClaimsJws(token).getBody(); return claims; } + public byte[] getSecretKeyBytes() { if (secretKeyBytes == null) { try { @@ -133,14 +137,4 @@ public class JwtTokenManager { } - /** - * get token claims - */ - public Claims getTokenClaims(String token) { - if (jwtParser == null) { - jwtParser = Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build(); - } - Claims claims = jwtParser.parseClaimsJws(token).getBody(); - return claims; - } -} \ No newline at end of file +} diff --git a/src/main/java/com/youlai/system/security/service/PermissionService.java b/src/main/java/com/youlai/system/security/service/PermissionService.java index f614c42e..413db07d 100644 --- a/src/main/java/com/youlai/system/security/service/PermissionService.java +++ b/src/main/java/com/youlai/system/security/service/PermissionService.java @@ -3,7 +3,7 @@ package com.youlai.system.security.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.youlai.system.common.constant.SecurityConstants; -import com.youlai.system.util.SecurityUtils; +import com.youlai.system.common.util.SecurityUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; diff --git a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java index 62d8a1b2..893f41c4 100644 --- a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java @@ -5,11 +5,10 @@ import cn.hutool.captcha.GifCaptcha; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.system.common.constant.SecurityConstants; +import com.youlai.system.security.jwt.JwtTokenProvider; import com.youlai.system.service.AuthService; -import com.youlai.system.util.RequestUtils; import com.youlai.system.model.dto.CaptchaResult; import com.youlai.system.model.dto.LoginResult; -import com.youlai.system.security.JwtTokenManager; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -34,8 +33,8 @@ import java.util.concurrent.TimeUnit; public class AuthServiceImpl implements AuthService { private final AuthenticationManager authenticationManager; - private final JwtTokenManager jwtTokenManager; private final RedisTemplate redisTemplate; + private final JwtTokenProvider jwtTokenProvider; /** * 登录 @@ -49,7 +48,7 @@ public class AuthServiceImpl implements AuthService { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password); Authentication authentication = authenticationManager.authenticate(authenticationToken); - String accessToken = jwtTokenManager.createToken(authentication); + String accessToken = jwtTokenProvider.createToken(authentication); return LoginResult.builder() .tokenType("Bearer") .accessToken(accessToken) @@ -62,9 +61,9 @@ public class AuthServiceImpl implements AuthService { @Override public void logout() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - String token = RequestUtils.resolveToken(request); + String token = jwtTokenProvider.resolveToken(request); if (StrUtil.isNotBlank(token)) { - Claims claims = jwtTokenManager.getTokenClaims(token); + Claims claims = jwtTokenProvider.getTokenClaims(token); String jti = claims.get("jti", String.class); Date expiration = claims.getExpiration(); if (expiration != null) { diff --git a/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java b/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java index 4f26cbdb..4841a44a 100644 --- a/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java @@ -20,7 +20,7 @@ import com.youlai.system.model.vo.RolePageVO; import com.youlai.system.service.SysRoleMenuService; import com.youlai.system.service.SysRoleService; import com.youlai.system.service.SysUserRoleService; -import com.youlai.system.util.SecurityUtils; +import com.youlai.system.common.util.SecurityUtils; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/youlai/system/service/impl/SysUserServiceImpl.java b/src/main/java/com/youlai/system/service/impl/SysUserServiceImpl.java index 6bba5173..4dbf4275 100644 --- a/src/main/java/com/youlai/system/service/impl/SysUserServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/SysUserServiceImpl.java @@ -11,7 +11,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.common.constant.SystemConstants; import com.youlai.system.converter.UserConverter; -import com.youlai.system.util.SecurityUtils; +import com.youlai.system.common.util.SecurityUtils; import com.youlai.system.mapper.SysUserMapper; import com.youlai.system.model.dto.UserAuthInfo; import com.youlai.system.model.bo.UserBO; diff --git a/src/main/java/com/youlai/system/util/RequestUtils.java b/src/main/java/com/youlai/system/util/RequestUtils.java deleted file mode 100644 index a6ecae57..00000000 --- a/src/main/java/com/youlai/system/util/RequestUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.youlai.system.util; - -import cn.hutool.core.util.StrUtil; -import com.youlai.system.common.constant.SecurityConstants; -import jakarta.servlet.http.HttpServletRequest; - -/** - * 请求工具类 - * - * @author haoxr - */ -public class RequestUtils { - - /** - * 请求头解析获取 Token - */ - public static String resolveToken(HttpServletRequest request) { - String bearerToken = request.getHeader(SecurityConstants.TOKEN_KEY); - if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith(SecurityConstants.TOKEN_PREFIX)) { - return bearerToken.substring(SecurityConstants.TOKEN_PREFIX.length()); - } - return null; - } -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 163f61d5..4f0b1efc 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -49,11 +49,11 @@ mybatis-plus: # 认证配置 -auth: - token: - secret_key: SecretKey012345678901234567890123456789012345678901234567890123456789 - # token 有效期(单位:秒) - ttl: 18000 +jwt: + # 签署密钥 + secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 + # token 过期时间(单位:秒) + expiration: 7200 oss: # OSS 类型 (目前支持aliyun、minio) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 5e53fa0f..897514ab 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -46,12 +46,11 @@ mybatis-plus: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 认证配置 -auth: - token: - secret_key: SecretKey012345678901234567890123456789012345678901234567890123456789 - # token 有效期(单位:秒) - ttl: 18000 - +jwt: + # 签署密钥 + secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 + # token 过期时间(单位:秒) + expiration: 7200 oss: # OSS 类型 (目前支持aliyun、minio)