提交 9403479a 编写于 作者: H hxr

refactor: 代码重构,移除 jjwt 的 JWT 库,使用 HuTool 工具实现 JWT 生成(默认jjwt库)、验证和解析。

上级 b1a4bb71
......@@ -80,6 +80,10 @@ youlai-boot
├── websocket # WebSocket 插件,实时双向通信
├── xxljob # XXL-JOB 插件,分布式任务调度和执行
├── service # 业务逻辑层
├── util # 工具类
├── JwtUtils # JWT 工具类
├── SecurityUtils # Spring Security 工具类
└── end
```
......
......@@ -6,7 +6,7 @@
<groupId>com.youlai</groupId>
<artifactId>youlai-boot</artifactId>
<version>2.5.3</version>
<version>2.6.0</version>
<description>基于 Java 17 + SpringBoot 3 构建的权限管理系统。</description>
<parent>
......@@ -33,8 +33,6 @@
<xxl-job.version>2.4.0</xxl-job.version>
<jjwt.version>0.11.5</jjwt.version>
<easyexcel.version>3.2.1</easyexcel.version>
<!-- 对象存储 -->
......@@ -119,16 +117,10 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
......@@ -153,25 +145,6 @@
<version>${xxl-job.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
......
......@@ -192,6 +192,7 @@ public class GlobalExceptionHandler {
throw e;
}
log.error("unknown exception: {}", e.getMessage());
e.printStackTrace();
return Result.failed(e.getLocalizedMessage());
}
......
......@@ -3,9 +3,8 @@ package com.youlai.system.config;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.core.security.exception.MyAccessDeniedHandler;
import com.youlai.system.core.security.exception.MyAuthenticationEntryPoint;
import com.youlai.system.core.security.jwt.JwtTokenFilter;
import com.youlai.system.filter.JwtTokenFilter;
import com.youlai.system.filter.VerifyCodeFilter;
import com.youlai.system.core.security.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -36,7 +35,6 @@ public class SecurityConfig {
private final MyAuthenticationEntryPoint authenticationEntryPoint;
private final MyAccessDeniedHandler accessDeniedHandler;
private final JwtTokenProvider jwtTokenProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
......@@ -58,7 +56,7 @@ public class SecurityConfig {
// 验证码校验过滤器
http.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
// JWT 校验过滤器
http.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
......
package com.youlai.system.config;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.core.security.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import com.youlai.system.util.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
......@@ -26,12 +25,9 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
*/
@Configuration
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
@RequiredArgsConstructor
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final JwtTokenProvider jwtTokenProvider;
/**
* 注册一个端点,客户端通过这个端点进行连接
*/
......@@ -83,8 +79,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
try {
// 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中
String tokenWithoutPrefix = bearerToken.substring(7);
String username = jwtTokenProvider.getUsername(tokenWithoutPrefix);
String username = JwtUtils.parseToken(bearerToken).get("name").toString();
if (StrUtil.isNotBlank(username)) {
accessor.setUser(() -> username);
......
......@@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.system.common.result.PageResult;
import com.youlai.system.common.result.Result;
import com.youlai.system.common.util.ExcelUtils;
import com.youlai.system.util.ExcelUtils;
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
import com.youlai.system.plugin.easyexcel.UserImportListener;
import com.youlai.system.model.vo.UserImportVO;
......
......@@ -23,7 +23,7 @@ import org.mapstruct.Mappings;
public interface UserConverter {
@Mappings({
@Mapping(target = "genderLabel", expression = "java(com.youlai.system.common.base.IBaseEnum.getLabelByValue(bo.getGender(), com.youlai.system.common.enums.GenderEnum.class))")
@Mapping(target = "genderLabel", expression = "java(com.youlai.system.common.base.IBaseEnum.getLabelByValue(bo.getGender(), com.youlai.system.enums.GenderEnum.class))")
})
UserPageVO toPageVo(UserBO bo);
......
......@@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.youlai.system.core.mybatis.annotation.DataPermission;
import com.youlai.system.common.base.IBaseEnum;
import com.youlai.system.common.enums.DataScopeEnum;
import com.youlai.system.common.util.SecurityUtils;
import com.youlai.system.enums.DataScopeEnum;
import com.youlai.system.util.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
......
package com.youlai.system.core.security.exception;
import com.youlai.system.common.result.ResultCode;
import com.youlai.system.common.util.ResponseUtils;
import com.youlai.system.util.ResponseUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
......
package com.youlai.system.core.security.exception;
import com.youlai.system.common.result.ResultCode;
import com.youlai.system.common.util.ResponseUtils;
import com.youlai.system.util.ResponseUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
......
package com.youlai.system.core.security.jwt;
import cn.hutool.core.convert.Convert;
import com.youlai.system.common.constant.JwtClaimConstants;
import com.youlai.system.core.security.model.SysUserDetails;
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.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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 java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* JWT token 工具类
* <p>
* 用于生成/校验/解析 JWT Token
*
* @author haoxr
* @since 2023/9/13
*/
@Component
public class JwtTokenProvider {
/**
* 签名密钥,用于签名 Access Token
*/
@Value("${jwt.secret-key:123456}")
private String secretKey;
@Value("${jwt.expiration:7200}")
private int expiration;
/**
* Base64 编码后的签名密钥,用于校验/解析 Access Token
*/
private byte[] secretKeyBytes;
/**
* 初始化方法
* <p>
* 对签名密钥进行 Base64 编码
*/
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
/**
* 创建Token
* <p>
* 认证成功后的用户信息会被封装到 Authentication 对象中,然后通过 JwtTokenProvider#createToken(Authentication) 方法创建 Token 字符串
*
* @param authentication 用户认证信息
* @return Token 字符串
*/
public String createToken(Authentication authentication) {
Claims claims = Jwts.claims().setSubject(authentication.getName());
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
claims.put(JwtClaimConstants.USER_ID, userDetails.getUserId()); // 用户ID
claims.put(JwtClaimConstants.USERNAME, claims.getSubject()); // 用户名
claims.put(JwtClaimConstants.DEPT_ID, userDetails.getDeptId()); // 部门ID
claims.put(JwtClaimConstants.DATA_SCOPE, userDetails.getDataScope()); // 数据权限范围
// claims 中添加角色信息
Set<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
claims.put(JwtClaimConstants.AUTHORITIES, roles);
Date now = new Date();
Date expirationTime = new Date(now.getTime() + expiration * 1000L);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expirationTime)
.signWith(Keys.hmacShaKeyFor(getSecretKeyBytes()), SignatureAlgorithm.HS256).compact();
}
/**
* 根据给定的令牌解析出用户认证信息
*
* @param token JWT Token
* @return 用户认证信息
*/
public Authentication getAuthentication(String token) {
Claims claims = this.getTokenClaims(token);
SysUserDetails userDetails = new SysUserDetails();
userDetails.setUserId(Convert.toLong(claims.get(JwtClaimConstants.USER_ID))); // 用户ID
userDetails.setUsername(Convert.toStr(claims.get(JwtClaimConstants.USERNAME))); // 用户名
userDetails.setDeptId(Convert.toLong(claims.get(JwtClaimConstants.DEPT_ID))); // 部门ID
userDetails.setDataScope(Convert.toInt(claims.get(JwtClaimConstants.DATA_SCOPE))); // 数据权限范围
// 角色集合
Set<SimpleGrantedAuthority> authorities = ((ArrayList<String>) claims.get(JwtClaimConstants.AUTHORITIES))
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
}
/**
* 从请求头中获取Token
*
* @param req 请求对象
* @return Token 字符串
*/
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader(HttpHeaders.AUTHORIZATION);
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
/**
* 校验Token是否有效
*
* @param token JWT Token
* @return 是否有效
*/
public boolean validateToken(String token) {
Jwts.parserBuilder().setSigningKey(getSecretKeyBytes()).build().parseClaimsJws(token);
return true;
}
/**
* 获取Token中的用户名
*
* @param token Token
* @return
*/
public String getUsername(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
/**
* 获取Token的Claims,claims中包含了用户的基本信息
*
* @param token
* @return
*/
public Claims getTokenClaims(String token) {
return Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build().parseClaimsJws(token).getBody();
}
/**
* 获取签名密钥的字节数组
*
* @return 签名密钥的字节数组
*/
public byte[] getSecretKeyBytes() {
if (secretKeyBytes == null) {
try {
secretKeyBytes = Decoders.BASE64.decode(secretKey);
} catch (DecodingException e) {
secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
}
}
return secretKeyBytes;
}
}
......@@ -3,7 +3,7 @@ package com.youlai.system.core.security.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.util.SecurityUtils;
import com.youlai.system.util.SecurityUtils;
import com.youlai.system.model.bo.RolePermsBO;
import com.youlai.system.service.SysRoleMenuService;
import jakarta.annotation.PostConstruct;
......
package com.youlai.system.common.enums;
package com.youlai.system.enums;
/**
* EasyCaptcha 验证码类型枚举
......
package com.youlai.system.common.enums;
package com.youlai.system.enums;
import com.youlai.system.common.base.IBaseEnum;
import lombok.Getter;
......
package com.youlai.system.common.enums;
package com.youlai.system.enums;
import com.youlai.system.common.base.IBaseEnum;
import io.swagger.v3.oas.annotations.media.Schema;
......
package com.youlai.system.common.enums;
package com.youlai.system.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.youlai.system.common.base.IBaseEnum;
......
package com.youlai.system.common.enums;
package com.youlai.system.enums;
import com.youlai.system.common.base.IBaseEnum;
import lombok.Getter;
......
package com.youlai.system.core.security.jwt;
package com.youlai.system.filter;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWTPayload;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.result.ResultCode;
import com.youlai.system.common.util.ResponseUtils;
import com.youlai.system.util.JwtUtils;
import com.youlai.system.util.ResponseUtils;
import com.youlai.system.common.exception.BusinessException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Map;
/**
* JWT token 过滤器
......@@ -21,15 +31,6 @@ import java.io.IOException;
*/
public class JwtTokenFilter extends OncePerRequestFilter {
/**
* JWT Token 工具类
*/
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
/**
* 从请求中获取 JWT Token,校验 JWT Token 是否合法
* <p>
......@@ -38,11 +39,21 @@ public class JwtTokenFilter extends OncePerRequestFilter {
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = jwtTokenProvider.resolveToken(request);
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
try {
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
if (StrUtil.isNotBlank(token)) {
Map<String, Object> payload = JwtUtils.parseToken(token);
String jti = Convert.toStr(payload.get(JWTPayload.JWT_ID));
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
Boolean isBlack = redisTemplate.hasKey(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti);
if (isBlack) {
ResponseUtils.writeErrMsg(response, ResultCode.TOKEN_INVALID);
return;
}
Authentication authentication = JwtUtils.getAuthentication(payload);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (BusinessException ex) {
//this is very important, since it guarantees the user is not authenticated at all
......
......@@ -6,7 +6,7 @@ import cn.hutool.extra.spring.SpringUtil;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.result.ResultCode;
import com.youlai.system.common.util.ResponseUtils;
import com.youlai.system.util.ResponseUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
......
package com.youlai.system.model.bo;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.enums.MenuTypeEnum;
import lombok.Data;
import java.util.List;
......
......@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.youlai.system.common.base.BaseEntity;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.enums.MenuTypeEnum;
import lombok.Data;
/**
......
package com.youlai.system.model.form;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.enums.MenuTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
......
package com.youlai.system.model.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.enums.MenuTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
......
package com.youlai.system.plugin.dupsubmit.aspect;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
import com.youlai.system.common.result.ResultCode;
import com.youlai.system.common.exception.BusinessException;
import com.youlai.system.core.security.jwt.JwtTokenProvider;
import com.youlai.system.util.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -14,6 +15,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
......@@ -33,11 +35,6 @@ import java.util.concurrent.TimeUnit;
public class DuplicateSubmitAspect {
private final RedissonClient redissonClient;
/**
* JWT token 工具类
*/
private final JwtTokenProvider jwtTokenProvider;
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
/**
......@@ -71,9 +68,9 @@ public class DuplicateSubmitAspect {
String resubmitLockKey = null;
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = jwtTokenProvider.resolveToken(request);
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
if (StrUtil.isNotBlank(token)) {
String jti = (String) jwtTokenProvider.getTokenClaims(token).get("jti");
String jti = Convert.toStr(JwtUtils.parseToken(token).get("jti"), null);
resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI();
}
return resubmitLockKey;
......
......@@ -9,8 +9,8 @@ import com.alibaba.excel.context.AnalysisContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.youlai.system.common.base.IBaseEnum;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.enums.GenderEnum;
import com.youlai.system.common.enums.StatusEnum;
import com.youlai.system.enums.GenderEnum;
import com.youlai.system.enums.StatusEnum;
import com.youlai.system.converter.UserConverter;
import com.youlai.system.model.entity.SysRole;
import com.youlai.system.model.entity.SysUser;
......
......@@ -3,20 +3,22 @@ package com.youlai.system.service.impl;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.enums.CaptchaTypeEnum;
import com.youlai.system.core.security.jwt.JwtTokenProvider;
import com.youlai.system.enums.CaptchaTypeEnum;
import com.youlai.system.model.dto.CaptchaResult;
import com.youlai.system.model.dto.LoginResult;
import com.youlai.system.plugin.captcha.CaptchaProperties;
import com.youlai.system.service.AuthService;
import io.jsonwebtoken.Claims;
import com.youlai.system.util.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
......@@ -26,7 +28,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.awt.*;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
......@@ -42,7 +44,6 @@ public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager;
private final StringRedisTemplate redisTemplate;
private final JwtTokenProvider jwtTokenProvider;
private final CodeGenerator codeGenerator;
private final Font captchaFont;
private final CaptchaProperties captchaProperties;
......@@ -59,7 +60,7 @@ public class AuthServiceImpl implements AuthService {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password);
Authentication authentication = authenticationManager.authenticate(authenticationToken);
String accessToken = jwtTokenProvider.createToken(authentication);
String accessToken = JwtUtils.generateToken(authentication);
return LoginResult.builder()
.tokenType("Bearer")
.accessToken(accessToken)
......@@ -72,15 +73,15 @@ public class AuthServiceImpl implements AuthService {
@Override
public void logout() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = jwtTokenProvider.resolveToken(request);
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
if (StrUtil.isNotBlank(token)) {
Claims claims = jwtTokenProvider.getTokenClaims(token);
String jti = claims.get("jti", String.class);
Date expiration = claims.getExpiration();
Map<String, Object> claims = JwtUtils.parseToken(token);
String jti = Convert.toStr(claims.get(JWTPayload.JWT_ID));
Long expiration = Convert.toLong(claims.get(JWTPayload.EXPIRES_AT));
if (expiration != null) {
long ttl = expiration.getTime() - System.currentTimeMillis();
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
long ttl = expiration - System.currentTimeMillis() / 1000;
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
}
......@@ -123,7 +124,7 @@ public class AuthServiceImpl implements AuthService {
// 验证码文本缓存至Redis,用于登录校验
String captchaKey = IdUtil.fastSimpleUUID();
redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey,captchaCode,
redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode,
captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);
return CaptchaResult.builder()
......
......@@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.enums.StatusEnum;
import com.youlai.system.enums.StatusEnum;
import com.youlai.system.converter.DeptConverter;
import com.youlai.system.mapper.SysDeptMapper;
import com.youlai.system.model.entity.SysDept;
......
......@@ -7,8 +7,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.common.enums.StatusEnum;
import com.youlai.system.enums.MenuTypeEnum;
import com.youlai.system.enums.StatusEnum;
import com.youlai.system.common.model.Option;
import com.youlai.system.converter.MenuConverter;
import com.youlai.system.mapper.SysMenuMapper;
......
......@@ -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.common.util.SecurityUtils;
import com.youlai.system.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
......
......@@ -8,12 +8,10 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.util.DateUtils;
import com.youlai.system.util.DateUtils;
import com.youlai.system.converter.UserConverter;
import com.youlai.system.common.util.SecurityUtils;
import com.youlai.system.util.SecurityUtils;
import com.youlai.system.mapper.SysUserMapper;
import com.youlai.system.model.dto.UserAuthInfo;
import com.youlai.system.model.bo.UserBO;
......@@ -32,7 +30,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
......
package com.youlai.system.common.util;
package com.youlai.system.util;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
......
package com.youlai.system.common.util;
package com.youlai.system.util;
import com.alibaba.excel.EasyExcel;
import com.youlai.system.plugin.easyexcel.MyAnalysisEventListener;
......
package com.youlai.system.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.youlai.system.common.constant.JwtClaimConstants;
import com.youlai.system.core.security.model.SysUserDetails;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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 java.util.*;
import java.util.stream.Collectors;
/**
* JWT 工具类
*
* @author haoxr
* @since 2.6.0
*/
@Component
public class JwtUtils {
private static byte[] key;
private static int ttl;
@Value("${jwt.key}")
public void setKey(String key) {
JwtUtils.key = key.getBytes();
}
@Value("${jwt.ttl}")
public void setTtl(Integer ttl) {
JwtUtils.ttl = ttl;
}
/**
* 创建Token
* <p>
* 认证成功后的用户信息会被封装到 Authentication 对象中,然后通过 JwtTokenProvider#createToken(Authentication) 方法创建 Token 字符串
*
* @param authentication 用户认证信息
* @return Token 字符串
*/
public static String generateToken(Authentication authentication) {
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
Map<String, Object> payload = new HashMap<>();
payload.put(JwtClaimConstants.USER_ID, userDetails.getUserId()); // 用户ID
payload.put(JwtClaimConstants.DEPT_ID, userDetails.getDeptId()); // 部门ID
payload.put(JwtClaimConstants.DATA_SCOPE, userDetails.getDataScope()); // 数据权限范围
// claims 中添加角色信息
Set<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
payload.put(JwtClaimConstants.AUTHORITIES, roles);
Date now = new Date();
Date expiration = DateUtil.offsetSecond(now, ttl);
payload.put(JWTPayload.ISSUED_AT, now);
payload.put(JWTPayload.EXPIRES_AT, expiration);
payload.put(JWTPayload.SUBJECT, authentication.getName());
payload.put(JWTPayload.JWT_ID, IdUtil.simpleUUID());
return JWTUtil.createToken(payload, JwtUtils.key);
}
/**
* 从 Token 中解析数据
*
* @param token JWT Token
* @return 解析数据
*/
public static Map<String, Object> parseToken(String token) {
try {
if (StrUtil.isBlank(token)) {
return null;
}
if (token.startsWith("Bearer ")) {
token = token.substring(7);
}
JWT jwt = JWTUtil.parseToken(token);
if (jwt.setKey(JwtUtils.key).validate(0)) {
return jwt.getPayloads();
}
} catch (Exception ignored) {
}
return null;
}
/**
* 从 Token 中获取 Authentication
*
* @param payload
* @return
*/
public static UsernamePasswordAuthenticationToken getAuthentication(Map<String, Object> payload) {
SysUserDetails userDetails = new SysUserDetails();
userDetails.setUserId(Convert.toLong(payload.get(JwtClaimConstants.USER_ID))); // 用户ID
userDetails.setDeptId(Convert.toLong(payload.get(JwtClaimConstants.DEPT_ID))); // 部门ID
userDetails.setDataScope(Convert.toInt(payload.get(JwtClaimConstants.DATA_SCOPE))); // 数据权限范围
userDetails.setUsername(Convert.toStr(payload.get(JWTPayload.SUBJECT))); // 用户名
// 角色集合
Set<SimpleGrantedAuthority> authorities = ((JSONArray) payload.get(JwtClaimConstants.AUTHORITIES))
.stream()
.map(authority -> new SimpleGrantedAuthority(Convert.toStr(authority)))
.collect(Collectors.toSet());
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
}
/**
* 验证 JWT Token
*
* @param token JWT Token
* @return 是否有效
*/
public static boolean verifyToken(String token) {
if (StrUtil.isBlank(token)) {
return false;
}
if (token.startsWith("Bearer ")) {
token = token.substring(7);
}
JWT jwt = JWTUtil.parseToken(token);
return jwt.setKey(JwtUtils.key).validate(0);
}
}
package com.youlai.system.common.util;
package com.youlai.system.util;
import cn.hutool.json.JSONUtil;
import com.youlai.system.common.result.IResultCode;
......
package com.youlai.system.common.util;
package com.youlai.system.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
......@@ -49,8 +49,7 @@ public class SecurityUtils {
* @return
*/
public static Long getDeptId() {
Long userId = Convert.toLong(getUser().getDeptId());
return userId;
return Convert.toLong(getUser().getDeptId());
}
/**
......@@ -59,8 +58,7 @@ public class SecurityUtils {
* @return DataScope
*/
public static Integer getDataScope() {
Integer dataScope = Convert.toInt(getUser().getDataScope());
return dataScope;
return Convert.toInt(getUser().getDataScope());
}
......@@ -74,10 +72,9 @@ public class SecurityUtils {
if (authentication != null) {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
if (CollectionUtil.isNotEmpty(authorities)) {
Set<String> roles = authorities.stream().filter(item -> item.getAuthority().startsWith("ROLE_"))
return authorities.stream().filter(item -> item.getAuthority().startsWith("ROLE_"))
.map(item -> StrUtil.removePrefix(item.getAuthority(), "ROLE_"))
.collect(Collectors.toSet());
return roles;
}
}
return Collections.EMPTY_SET;
......@@ -93,10 +90,10 @@ public class SecurityUtils {
if (authentication != null) {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
if (CollectionUtil.isNotEmpty(authorities)) {
Set<String> perms = authorities.stream().filter(item -> !item.getAuthority().startsWith("ROLE_"))
.map(item -> item.getAuthority())
return authorities.stream()
.map(GrantedAuthority::getAuthority)
.filter(authority -> !authority.startsWith("ROLE_"))
.collect(Collectors.toSet());
return perms;
}
}
return Collections.EMPTY_SET;
......@@ -111,11 +108,7 @@ public class SecurityUtils {
*/
public static boolean isRoot() {
Set<String> roles = getRoles();
if (roles.contains(SystemConstants.ROOT_ROLE_CODE)) {
return true;
}
return false;
return roles.contains(SystemConstants.ROOT_ROLE_CODE);
}
......@@ -134,8 +127,7 @@ public class SecurityUtils {
Set<String> perms = getPerms();
boolean hasPerm = perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
return hasPerm;
return perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
}
}
......@@ -59,10 +59,10 @@ mybatis-plus:
# 认证配置
jwt:
# 签署密钥
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
# 密钥
key: SecretKey012345678901234567890123456789012345678901234567890123456789
# token 过期时间(单位:秒)
expiration: 7200
ttl: 7200
oss:
# OSS 类型 (目前支持aliyun、minio)
......
......@@ -59,10 +59,10 @@ mybatis-plus:
# 认证配置
jwt:
# 签署密钥
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
# 密钥
key: SecretKey012345678901234567890123456789012345678901234567890123456789
# token 过期时间(单位:秒)
expiration: 7200
ttl: 7200
oss:
# OSS 类型 (目前支持aliyun、minio)
......
......@@ -44,7 +44,7 @@
LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id
LEFT JOIN sys_role t3 ON t2.role_id = t3.id
WHERE
t1.type != '${@com.youlai.system.common.enums.MenuTypeEnum@BUTTON.getValue()}'
t1.type != '${@com.youlai.system.enums.MenuTypeEnum@BUTTON.getValue()}'
ORDER BY t1.sort asc
</select>
......@@ -57,7 +57,7 @@
INNER JOIN sys_role_menu t2 ON t1.id = t2.menu_id
INNER JOIN sys_role t3 ON t3.id = t2.role_id
WHERE
t1.type = '${@com.youlai.system.common.enums.MenuTypeEnum@BUTTON.getValue()}'
t1.type = '${@com.youlai.system.enums.MenuTypeEnum@BUTTON.getValue()}'
AND t1.perm IS NOT NULL
<choose>
<when test="roles!=null and roles.size()>0">
......
......@@ -33,7 +33,7 @@
INNER JOIN sys_role t2 ON t1.role_id = t2.id AND t2.deleted = 0
INNER JOIN sys_menu t3 ON t1.menu_id = t3.id
WHERE
type = '${@com.youlai.system.common.enums.MenuTypeEnum@BUTTON.getValue()}'
type = '${@com.youlai.system.enums.MenuTypeEnum@BUTTON.getValue()}'
<if test="roleCode!=null and roleCode.trim() neq ''">
AND t2.`code` = #{roleCode}
</if>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册