refactor: SpringSecurity + JWT 认证鉴权重构

This commit is contained in:
haoxr
2023-09-13 18:27:15 +08:00
parent ae59e5251e
commit 3ff462305b
21 changed files with 160 additions and 214 deletions

View File

@@ -1,11 +1,10 @@
package com.youlai.system.aspect; package com.youlai.system.aspect;
import cn.hutool.core.util.StrUtil; 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.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 jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -25,7 +24,7 @@ import java.util.concurrent.TimeUnit;
* 处理重复提交的切面 * 处理重复提交的切面
* *
* @author haoxr * @author haoxr
* @since 3.0.0 * @since 2.3.0
*/ */
@Aspect @Aspect
@Component @Component
@@ -35,7 +34,7 @@ public class DuplicateSubmitAspect {
private final RedissonClient redissonClient; private final RedissonClient redissonClient;
private final JwtTokenManager jwtTokenManager; private final JwtTokenProvider jwtTokenProvider;
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:"; private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
/** /**
@@ -68,9 +67,10 @@ public class DuplicateSubmitAspect {
private String generateResubmitLockKey() { private String generateResubmitLockKey() {
String resubmitLockKey = null; String resubmitLockKey = null;
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String jwt = RequestUtils.resolveToken(request);
if (StrUtil.isNotBlank(jwt)) { String token = jwtTokenProvider.resolveToken(request);
String jti = (String) jwtTokenManager.getTokenClaims(jwt).get("jti"); if (StrUtil.isNotBlank(token)) {
String jti = (String) jwtTokenProvider.getTokenClaims(token).get("jti");
resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI(); resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI();
} }
return resubmitLockKey; return resubmitLockKey;

View File

@@ -4,10 +4,8 @@ import java.lang.annotation.*;
/** /**
* MP数据权限注解 * MP数据权限注解
* <p>
* *
* @author zc * @author zc
* @link https://gitee.com/baomidou/mybatis-plus/issues/I37I90
* @since 2.0.0 * @since 2.0.0
*/ */
@Documented @Documented

View File

@@ -1,4 +1,4 @@
package com.youlai.system.util; package com.youlai.system.common.util;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.youlai.system.listener.easyexcel.MyAnalysisEventListener; import com.youlai.system.listener.easyexcel.MyAnalysisEventListener;

View File

@@ -1,6 +1,7 @@
package com.youlai.system.util; package com.youlai.system.common.util;
import cn.hutool.json.JSONUtil; 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.Result;
import com.youlai.system.common.result.ResultCode; import com.youlai.system.common.result.ResultCode;
@@ -10,6 +11,8 @@ import org.springframework.http.MediaType;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import static com.youlai.system.common.result.ResultCode.*;
/** /**
* 响应工具类 * 响应工具类
* *

View File

@@ -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.collection.CollectionUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;

View File

@@ -1,11 +1,11 @@
package com.youlai.system.config; package com.youlai.system.config;
import com.youlai.system.common.constant.SecurityConstants; 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.MyAccessDeniedHandler;
import com.youlai.system.security.exception.MyAuthenticationEntryPoint; 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.filter.VerifyCodeFilter;
import com.youlai.system.security.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -36,7 +36,7 @@ public class SecurityConfig {
private final MyAuthenticationEntryPoint authenticationEntryPoint; private final MyAuthenticationEntryPoint authenticationEntryPoint;
private final MyAccessDeniedHandler accessDeniedHandler; private final MyAccessDeniedHandler accessDeniedHandler;
private final JwtTokenManager jwtTokenManager; private final JwtTokenProvider jwtTokenProvider;
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
@@ -58,7 +58,7 @@ public class SecurityConfig {
// 验证码校验过滤器 // 验证码校验过滤器
http.addFilterBefore(new VerifyCodeFilter(),UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(new VerifyCodeFilter(),UsernamePasswordAuthenticationFilter.class);
// JWT 校验过滤器 // JWT 校验过滤器
http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenManager), UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build(); return http.build();
} }

View File

@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.system.common.constant.ExcelConstants; import com.youlai.system.common.constant.ExcelConstants;
import com.youlai.system.common.result.PageResult; import com.youlai.system.common.result.PageResult;
import com.youlai.system.common.result.Result; 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.common.annotation.PreventDuplicateSubmit;
import com.youlai.system.listener.easyexcel.UserImportListener; import com.youlai.system.listener.easyexcel.UserImportListener;
import com.youlai.system.model.vo.UserImportVO; import com.youlai.system.model.vo.UserImportVO;

View File

@@ -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);
}
}
}
}

View File

@@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.result.ResultCode; 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.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;

View File

@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.youlai.system.common.annotation.DataPermission; import com.youlai.system.common.annotation.DataPermission;
import com.youlai.system.common.base.IBaseEnum; import com.youlai.system.common.base.IBaseEnum;
import com.youlai.system.common.enums.DataScopeEnum; 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.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;

View File

@@ -1,9 +1,7 @@
package com.youlai.system.interceptor; package com.youlai.system.interceptor;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.security.jwt.JwtTokenProvider;
import com.youlai.system.security.JwtTokenManager;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
@@ -25,13 +23,13 @@ import java.security.Principal;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AuthChannelInterceptor implements ChannelInterceptor { public class AuthChannelInterceptor implements ChannelInterceptor {
private final JwtTokenManager jwtTokenManager; private final JwtTokenProvider jwtTokenProvider;
/** /**
* 连接前监听 * 连接前监听
* *
* @param message * @param message 消息
* @param channel * @param channel 通道
* @return * @return
*/ */
@Override @Override
@@ -41,14 +39,12 @@ public class AuthChannelInterceptor implements ChannelInterceptor {
if (StompCommand.CONNECT.equals(accessor.getCommand())) { if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// get token from header // get token from header
String token = accessor.getFirstNativeHeader("Authorization"); String bearerToken = accessor.getFirstNativeHeader("Authorization");
// if token is not null // if token is not null
if (StrUtil.isNotBlank(token)) { if (StrUtil.isNotBlank(bearerToken)) {
token = token.substring(SecurityConstants.TOKEN_PREFIX.length()); bearerToken = bearerToken.substring(7);
Claims claims = jwtTokenManager.parseAndValidateToken(token); String username = jwtTokenProvider.getUsername(bearerToken);
String username = claims.get("username", String.class);
// if the username is not null, assign it to the Principal. // if the username is not null, assign it to the Principal.
if (StrUtil.isNotBlank(username)) { if (StrUtil.isNotBlank(username)) {
Principal principal = () -> username; Principal principal = () -> username;

View File

@@ -1,7 +1,7 @@
package com.youlai.system.security.exception; package com.youlai.system.security.exception;
import com.youlai.system.common.result.ResultCode; 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.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -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);
}
}

View File

@@ -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.convert.Convert;
import cn.hutool.core.util.IdUtil;
import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.security.model.SysUserDetails; 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.Decoders;
import io.jsonwebtoken.io.DecodingException; import io.jsonwebtoken.io.DecodingException;
import io.jsonwebtoken.security.Keys; 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.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 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.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* JWT token 管理器 * JWT token 管理器
* *
* @author haoxr * @author haoxr
* @since 2022/10/22 * @since 2023/9/13
*/ */
@Component @Component
public class JwtTokenManager { public class JwtTokenProvider {
/**
* 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;
@Resource @Resource
private RedisTemplate redisTemplate; 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 * @param authentication
* @return token * @return
*/ */
public String createToken(Authentication authentication) { public String createToken(Authentication authentication) {
Claims claims = Jwts.claims().setSubject(authentication.getName()); Claims claims = Jwts.claims().setSubject(authentication.getName());
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal(); SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
claims.put("jti",IdUtil.fastSimpleUUID());
claims.put("userId", userDetails.getUserId()); claims.put("userId", userDetails.getUserId());
claims.put("username", claims.getSubject()); claims.put("username", claims.getSubject());
claims.put("deptId", userDetails.getDeptId()); claims.put("deptId", userDetails.getDeptId());
claims.put("dataScope", userDetails.getDataScope()); claims.put("dataScope", userDetails.getDataScope());
// 角色放入JWT的claims
Set<String> roles = userDetails.getAuthorities().stream() Set<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); .map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
claims.put("authorities", roles); claims.put("authorities", roles);
@@ -79,48 +74,57 @@ public class JwtTokenManager {
Set<String> perms = userDetails.getPerms(); Set<String> perms = userDetails.getPerms();
redisTemplate.opsForValue().set(SecurityConstants.USER_PERMS_CACHE_PREFIX + userDetails.getUserId(), perms); 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() return Jwts.builder()
//.setId(IdUtil.fastSimpleUUID()) TODO 设置jti无效
.setClaims(claims) .setClaims(claims)
.setIssuedAt(now)
.setExpiration(expirationTime) .setExpiration(expirationTime)
.signWith(Keys.hmacShaKeyFor(this.getSecretKeyBytes()), SignatureAlgorithm.HS256).compact(); .signWith(Keys.hmacShaKeyFor(getSecretKeyBytes()), SignatureAlgorithm.HS256).compact();
} }
/** public Authentication getAuthentication(String token) {
* 获取认证信息 Claims claims = this.getTokenClaims(token);
*/
public Authentication getAuthentication( Claims claims) { SysUserDetails userDetails = new SysUserDetails();
SysUserDetails principal = new SysUserDetails(); userDetails.setUserId(Convert.toLong(claims.get("userId"))); // 用户ID
principal.setUserId(Convert.toLong(claims.get("userId"))); // 用户ID userDetails.setUsername(Convert.toStr(claims.get("username"))); // 用户
principal.setUsername(Convert.toStr(claims.get("username"))); // 用户名 userDetails.setDeptId(Convert.toLong(claims.get("deptId"))); // 部门ID
principal.setDeptId(Convert.toLong(claims.get("deptId"))); // 部门ID userDetails.setDataScope(Convert.toInt(claims.get("dataScope"))); // 数据权限范围
principal.setDataScope(Convert.toInt(claims.get("dataScope"))); // 数据权限
List<SimpleGrantedAuthority> authorities = ((ArrayList<String>) claims.get("authorities")) List<SimpleGrantedAuthority> authorities = ((ArrayList<String>) claims.get("authorities"))
.stream() .stream()
.map(role -> new SimpleGrantedAuthority(role)) .map(SimpleGrantedAuthority::new)
.collect(Collectors.toList()); .toList();
return new UsernamePasswordAuthenticationToken(principal, "", authorities); return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
} }
/** public String resolveToken(HttpServletRequest req) {
* 解析 & 验证 token String bearerToken = req.getHeader("Authorization");
*/ if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
public Claims parseAndValidateToken(String token) { return bearerToken.substring(7);
// 解析成功说明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 已被禁用");
} }
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; return claims;
} }
public byte[] getSecretKeyBytes() { public byte[] getSecretKeyBytes() {
if (secretKeyBytes == null) { if (secretKeyBytes == null) {
try { 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;
}
} }

View File

@@ -3,7 +3,7 @@ package com.youlai.system.security.service;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants; 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.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;

View File

@@ -5,11 +5,10 @@ import cn.hutool.captcha.GifCaptcha;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants; import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.security.jwt.JwtTokenProvider;
import com.youlai.system.service.AuthService; 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.CaptchaResult;
import com.youlai.system.model.dto.LoginResult; import com.youlai.system.model.dto.LoginResult;
import com.youlai.system.security.JwtTokenManager;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -34,8 +33,8 @@ import java.util.concurrent.TimeUnit;
public class AuthServiceImpl implements AuthService { public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final JwtTokenManager jwtTokenManager;
private final RedisTemplate redisTemplate; private final RedisTemplate redisTemplate;
private final JwtTokenProvider jwtTokenProvider;
/** /**
* 登录 * 登录
@@ -49,7 +48,7 @@ public class AuthServiceImpl implements AuthService {
UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password); new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password);
Authentication authentication = authenticationManager.authenticate(authenticationToken); Authentication authentication = authenticationManager.authenticate(authenticationToken);
String accessToken = jwtTokenManager.createToken(authentication); String accessToken = jwtTokenProvider.createToken(authentication);
return LoginResult.builder() return LoginResult.builder()
.tokenType("Bearer") .tokenType("Bearer")
.accessToken(accessToken) .accessToken(accessToken)
@@ -62,9 +61,9 @@ public class AuthServiceImpl implements AuthService {
@Override @Override
public void logout() { public void logout() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = RequestUtils.resolveToken(request); String token = jwtTokenProvider.resolveToken(request);
if (StrUtil.isNotBlank(token)) { if (StrUtil.isNotBlank(token)) {
Claims claims = jwtTokenManager.getTokenClaims(token); Claims claims = jwtTokenProvider.getTokenClaims(token);
String jti = claims.get("jti", String.class); String jti = claims.get("jti", String.class);
Date expiration = claims.getExpiration(); Date expiration = claims.getExpiration();
if (expiration != null) { if (expiration != null) {

View File

@@ -20,7 +20,7 @@ import com.youlai.system.model.vo.RolePageVO;
import com.youlai.system.service.SysRoleMenuService; import com.youlai.system.service.SysRoleMenuService;
import com.youlai.system.service.SysRoleService; import com.youlai.system.service.SysRoleService;
import com.youlai.system.service.SysUserRoleService; import com.youlai.system.service.SysUserRoleService;
import com.youlai.system.util.SecurityUtils; import com.youlai.system.common.util.SecurityUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@@ -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.SecurityConstants;
import com.youlai.system.common.constant.SystemConstants; import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.converter.UserConverter; 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.mapper.SysUserMapper;
import com.youlai.system.model.dto.UserAuthInfo; import com.youlai.system.model.dto.UserAuthInfo;
import com.youlai.system.model.bo.UserBO; import com.youlai.system.model.bo.UserBO;

View File

@@ -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;
}
}

View File

@@ -49,11 +49,11 @@ mybatis-plus:
# 认证配置 # 认证配置
auth: jwt:
token: # 签署密钥
secret_key: SecretKey012345678901234567890123456789012345678901234567890123456789 secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
# token 有效期(单位:秒) # token 过期时间(单位:秒)
ttl: 18000 expiration: 7200
oss: oss:
# OSS 类型 (目前支持aliyun、minio) # OSS 类型 (目前支持aliyun、minio)

View File

@@ -46,12 +46,11 @@ mybatis-plus:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 认证配置 # 认证配置
auth: jwt:
token: # 签署密钥
secret_key: SecretKey012345678901234567890123456789012345678901234567890123456789 secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
# token 有效期(单位:秒) # token 过期时间(单位:秒)
ttl: 18000 expiration: 7200
oss: oss:
# OSS 类型 (目前支持aliyun、minio) # OSS 类型 (目前支持aliyun、minio)