refactor: 代码重构,移除 jjwt 的 JWT 库,使用 HuTool 工具实现 JWT 生成(默认jjwt库)、验证和解析。
This commit is contained in:
@@ -80,6 +80,10 @@ youlai-boot
|
|||||||
├── websocket # WebSocket 插件,实时双向通信
|
├── websocket # WebSocket 插件,实时双向通信
|
||||||
├── xxljob # XXL-JOB 插件,分布式任务调度和执行
|
├── xxljob # XXL-JOB 插件,分布式任务调度和执行
|
||||||
├── service # 业务逻辑层
|
├── service # 业务逻辑层
|
||||||
|
├── util # 工具类
|
||||||
|
├── JwtUtils # JWT 工具类
|
||||||
|
├── SecurityUtils # Spring Security 工具类
|
||||||
|
|
||||||
└── end
|
└── end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
31
pom.xml
31
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.youlai</groupId>
|
<groupId>com.youlai</groupId>
|
||||||
<artifactId>youlai-boot</artifactId>
|
<artifactId>youlai-boot</artifactId>
|
||||||
<version>2.5.3</version>
|
<version>2.6.0</version>
|
||||||
<description>基于 Java 17 + SpringBoot 3 构建的权限管理系统。</description>
|
<description>基于 Java 17 + SpringBoot 3 构建的权限管理系统。</description>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
<xxl-job.version>2.4.0</xxl-job.version>
|
<xxl-job.version>2.4.0</xxl-job.version>
|
||||||
|
|
||||||
<jjwt.version>0.11.5</jjwt.version>
|
|
||||||
|
|
||||||
<easyexcel.version>3.2.1</easyexcel.version>
|
<easyexcel.version>3.2.1</easyexcel.version>
|
||||||
|
|
||||||
<!-- 对象存储 -->
|
<!-- 对象存储 -->
|
||||||
@@ -119,16 +117,10 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mybatis</groupId>
|
|
||||||
<artifactId>mybatis-spring</artifactId>
|
|
||||||
<version>3.0.3</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.xiaoymin</groupId>
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||||
@@ -153,25 +145,6 @@
|
|||||||
<version>${xxl-job.version}</version>
|
<version>${xxl-job.version}</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>easyexcel</artifactId>
|
<artifactId>easyexcel</artifactId>
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ public class GlobalExceptionHandler {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
log.error("unknown exception: {}", e.getMessage());
|
log.error("unknown exception: {}", e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
return Result.failed(e.getLocalizedMessage());
|
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.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.core.security.exception.MyAccessDeniedHandler;
|
import com.youlai.system.core.security.exception.MyAccessDeniedHandler;
|
||||||
import com.youlai.system.core.security.exception.MyAuthenticationEntryPoint;
|
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.filter.VerifyCodeFilter;
|
||||||
import com.youlai.system.core.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 +35,6 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
private final MyAuthenticationEntryPoint authenticationEntryPoint;
|
private final MyAuthenticationEntryPoint authenticationEntryPoint;
|
||||||
private final MyAccessDeniedHandler accessDeniedHandler;
|
private final MyAccessDeniedHandler accessDeniedHandler;
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
@@ -58,7 +56,7 @@ public class SecurityConfig {
|
|||||||
// 验证码校验过滤器
|
// 验证码校验过滤器
|
||||||
http.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
// JWT 校验过滤器
|
// JWT 校验过滤器
|
||||||
http.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
|
http.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.youlai.system.config;
|
package com.youlai.system.config;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.core.security.jwt.JwtTokenProvider;
|
import com.youlai.system.util.JwtUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -26,12 +25,9 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
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 ")) {
|
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||||
try {
|
try {
|
||||||
// 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中
|
// 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中
|
||||||
String tokenWithoutPrefix = bearerToken.substring(7);
|
String username = JwtUtils.parseToken(bearerToken).get("name").toString();
|
||||||
String username = jwtTokenProvider.getUsername(tokenWithoutPrefix);
|
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(username)) {
|
if (StrUtil.isNotBlank(username)) {
|
||||||
accessor.setUser(() -> 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.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
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.common.util.ExcelUtils;
|
import com.youlai.system.util.ExcelUtils;
|
||||||
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
|
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
|
||||||
import com.youlai.system.plugin.easyexcel.UserImportListener;
|
import com.youlai.system.plugin.easyexcel.UserImportListener;
|
||||||
import com.youlai.system.model.vo.UserImportVO;
|
import com.youlai.system.model.vo.UserImportVO;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.mapstruct.Mappings;
|
|||||||
public interface UserConverter {
|
public interface UserConverter {
|
||||||
|
|
||||||
@Mappings({
|
@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);
|
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.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
||||||
import com.youlai.system.core.mybatis.annotation.DataPermission;
|
import com.youlai.system.core.mybatis.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.enums.DataScopeEnum;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.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;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.core.security.exception;
|
package com.youlai.system.core.security.exception;
|
||||||
|
|
||||||
import com.youlai.system.common.result.ResultCode;
|
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.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;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.core.security.exception;
|
package com.youlai.system.core.security.exception;
|
||||||
|
|
||||||
import com.youlai.system.common.result.ResultCode;
|
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.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
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.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.CacheConstants;
|
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.model.bo.RolePermsBO;
|
||||||
import com.youlai.system.service.SysRoleMenuService;
|
import com.youlai.system.service.SysRoleMenuService;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.enums;
|
package com.youlai.system.enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EasyCaptcha 验证码类型枚举
|
* EasyCaptcha 验证码类型枚举
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.enums;
|
package com.youlai.system.enums;
|
||||||
|
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.enums;
|
package com.youlai.system.enums;
|
||||||
|
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.enums;
|
package com.youlai.system.enums;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.enums;
|
package com.youlai.system.enums;
|
||||||
|
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
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.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 com.youlai.system.common.exception.BusinessException;
|
||||||
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;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
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.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT token 过滤器
|
* JWT token 过滤器
|
||||||
@@ -21,15 +31,6 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public class JwtTokenFilter extends OncePerRequestFilter {
|
public class JwtTokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
/**
|
|
||||||
* JWT Token 工具类
|
|
||||||
*/
|
|
||||||
private JwtTokenProvider jwtTokenProvider;
|
|
||||||
|
|
||||||
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
|
|
||||||
this.jwtTokenProvider = jwtTokenProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从请求中获取 JWT Token,校验 JWT Token 是否合法
|
* 从请求中获取 JWT Token,校验 JWT Token 是否合法
|
||||||
* <p>
|
* <p>
|
||||||
@@ -38,11 +39,21 @@ public class JwtTokenFilter extends OncePerRequestFilter {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
String token = jwtTokenProvider.resolveToken(request);
|
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
try {
|
try {
|
||||||
if (token != null && jwtTokenProvider.validateToken(token)) {
|
if (StrUtil.isNotBlank(token)) {
|
||||||
Authentication auth = jwtTokenProvider.getAuthentication(token);
|
Map<String, Object> payload = JwtUtils.parseToken(token);
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
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) {
|
} catch (BusinessException ex) {
|
||||||
//this is very important, since it guarantees the user is not authenticated at all
|
//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.CacheConstants;
|
||||||
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.common.util.ResponseUtils;
|
import com.youlai.system.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;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.youlai.system.model.bo;
|
package com.youlai.system.model.bo;
|
||||||
|
|
||||||
import com.youlai.system.common.enums.MenuTypeEnum;
|
import com.youlai.system.enums.MenuTypeEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
|
||||||
import com.youlai.system.common.base.BaseEntity;
|
import com.youlai.system.common.base.BaseEntity;
|
||||||
import com.youlai.system.common.enums.MenuTypeEnum;
|
import com.youlai.system.enums.MenuTypeEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.model.form;
|
package com.youlai.system.model.form;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
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 io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.model.vo;
|
package com.youlai.system.model.vo;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
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 io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.youlai.system.plugin.dupsubmit.aspect;
|
package com.youlai.system.plugin.dupsubmit.aspect;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
|
import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit;
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.exception.BusinessException;
|
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 jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -14,6 +15,7 @@ import org.aspectj.lang.annotation.Aspect;
|
|||||||
import org.aspectj.lang.annotation.Pointcut;
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
import org.redisson.api.RLock;
|
import org.redisson.api.RLock;
|
||||||
import org.redisson.api.RedissonClient;
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
@@ -33,11 +35,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class DuplicateSubmitAspect {
|
public class DuplicateSubmitAspect {
|
||||||
|
|
||||||
private final RedissonClient redissonClient;
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
/**
|
|
||||||
* JWT token 工具类
|
|
||||||
*/
|
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
|
||||||
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
|
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,9 +68,9 @@ public class DuplicateSubmitAspect {
|
|||||||
String resubmitLockKey = null;
|
String resubmitLockKey = null;
|
||||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
|
||||||
String token = jwtTokenProvider.resolveToken(request);
|
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (StrUtil.isNotBlank(token)) {
|
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();
|
resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI();
|
||||||
}
|
}
|
||||||
return resubmitLockKey;
|
return resubmitLockKey;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import com.alibaba.excel.context.AnalysisContext;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
import com.youlai.system.common.constant.SystemConstants;
|
||||||
import com.youlai.system.common.enums.GenderEnum;
|
import com.youlai.system.enums.GenderEnum;
|
||||||
import com.youlai.system.common.enums.StatusEnum;
|
import com.youlai.system.enums.StatusEnum;
|
||||||
import com.youlai.system.converter.UserConverter;
|
import com.youlai.system.converter.UserConverter;
|
||||||
import com.youlai.system.model.entity.SysRole;
|
import com.youlai.system.model.entity.SysRole;
|
||||||
import com.youlai.system.model.entity.SysUser;
|
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.AbstractCaptcha;
|
||||||
import cn.hutool.captcha.CaptchaUtil;
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
import cn.hutool.captcha.generator.CodeGenerator;
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
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 cn.hutool.jwt.JWTPayload;
|
||||||
import com.youlai.system.common.constant.CacheConstants;
|
import com.youlai.system.common.constant.CacheConstants;
|
||||||
import com.youlai.system.common.enums.CaptchaTypeEnum;
|
import com.youlai.system.enums.CaptchaTypeEnum;
|
||||||
import com.youlai.system.core.security.jwt.JwtTokenProvider;
|
|
||||||
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.plugin.captcha.CaptchaProperties;
|
import com.youlai.system.plugin.captcha.CaptchaProperties;
|
||||||
import com.youlai.system.service.AuthService;
|
import com.youlai.system.service.AuthService;
|
||||||
import io.jsonwebtoken.Claims;
|
import com.youlai.system.util.JwtUtils;
|
||||||
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;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
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 org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Date;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,7 +44,6 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
|
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final StringRedisTemplate redisTemplate;
|
private final StringRedisTemplate redisTemplate;
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
|
||||||
private final CodeGenerator codeGenerator;
|
private final CodeGenerator codeGenerator;
|
||||||
private final Font captchaFont;
|
private final Font captchaFont;
|
||||||
private final CaptchaProperties captchaProperties;
|
private final CaptchaProperties captchaProperties;
|
||||||
@@ -59,7 +60,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 = jwtTokenProvider.createToken(authentication);
|
String accessToken = JwtUtils.generateToken(authentication);
|
||||||
return LoginResult.builder()
|
return LoginResult.builder()
|
||||||
.tokenType("Bearer")
|
.tokenType("Bearer")
|
||||||
.accessToken(accessToken)
|
.accessToken(accessToken)
|
||||||
@@ -72,15 +73,15 @@ 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 = jwtTokenProvider.resolveToken(request);
|
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (StrUtil.isNotBlank(token)) {
|
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) {
|
if (expiration != null) {
|
||||||
long ttl = expiration.getTime() - System.currentTimeMillis();
|
long ttl = expiration - System.currentTimeMillis() / 1000;
|
||||||
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
|
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.SECONDS);
|
||||||
} else {
|
} else {
|
||||||
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
|
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
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.converter.DeptConverter;
|
||||||
import com.youlai.system.mapper.SysDeptMapper;
|
import com.youlai.system.mapper.SysDeptMapper;
|
||||||
import com.youlai.system.model.entity.SysDept;
|
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.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
import com.youlai.system.common.constant.SystemConstants;
|
||||||
import com.youlai.system.common.enums.MenuTypeEnum;
|
import com.youlai.system.enums.MenuTypeEnum;
|
||||||
import com.youlai.system.common.enums.StatusEnum;
|
import com.youlai.system.enums.StatusEnum;
|
||||||
import com.youlai.system.common.model.Option;
|
import com.youlai.system.common.model.Option;
|
||||||
import com.youlai.system.converter.MenuConverter;
|
import com.youlai.system.converter.MenuConverter;
|
||||||
import com.youlai.system.mapper.SysMenuMapper;
|
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.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.common.util.SecurityUtils;
|
import com.youlai.system.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;
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
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.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.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.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;
|
||||||
@@ -32,7 +30,6 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.youlai.system.plugin.easyexcel.MyAnalysisEventListener;
|
import com.youlai.system.plugin.easyexcel.MyAnalysisEventListener;
|
||||||
152
src/main/java/com/youlai/system/util/JwtUtils.java
Normal file
152
src/main/java/com/youlai/system/util/JwtUtils.java
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.youlai.system.common.result.IResultCode;
|
import com.youlai.system.common.result.IResultCode;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.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;
|
||||||
@@ -49,8 +49,7 @@ public class SecurityUtils {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Long getDeptId() {
|
public static Long getDeptId() {
|
||||||
Long userId = Convert.toLong(getUser().getDeptId());
|
return Convert.toLong(getUser().getDeptId());
|
||||||
return userId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,8 +58,7 @@ public class SecurityUtils {
|
|||||||
* @return DataScope
|
* @return DataScope
|
||||||
*/
|
*/
|
||||||
public static Integer getDataScope() {
|
public static Integer getDataScope() {
|
||||||
Integer dataScope = Convert.toInt(getUser().getDataScope());
|
return Convert.toInt(getUser().getDataScope());
|
||||||
return dataScope;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -74,10 +72,9 @@ public class SecurityUtils {
|
|||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
if (CollectionUtil.isNotEmpty(authorities)) {
|
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_"))
|
.map(item -> StrUtil.removePrefix(item.getAuthority(), "ROLE_"))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
return roles;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Collections.EMPTY_SET;
|
return Collections.EMPTY_SET;
|
||||||
@@ -93,10 +90,10 @@ public class SecurityUtils {
|
|||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
if (CollectionUtil.isNotEmpty(authorities)) {
|
if (CollectionUtil.isNotEmpty(authorities)) {
|
||||||
Set<String> perms = authorities.stream().filter(item -> !item.getAuthority().startsWith("ROLE_"))
|
return authorities.stream()
|
||||||
.map(item -> item.getAuthority())
|
.map(GrantedAuthority::getAuthority)
|
||||||
|
.filter(authority -> !authority.startsWith("ROLE_"))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
return perms;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Collections.EMPTY_SET;
|
return Collections.EMPTY_SET;
|
||||||
@@ -111,11 +108,7 @@ public class SecurityUtils {
|
|||||||
*/
|
*/
|
||||||
public static boolean isRoot() {
|
public static boolean isRoot() {
|
||||||
Set<String> roles = getRoles();
|
Set<String> roles = getRoles();
|
||||||
|
return roles.contains(SystemConstants.ROOT_ROLE_CODE);
|
||||||
if (roles.contains(SystemConstants.ROOT_ROLE_CODE)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -134,8 +127,7 @@ public class SecurityUtils {
|
|||||||
|
|
||||||
Set<String> perms = getPerms();
|
Set<String> perms = getPerms();
|
||||||
|
|
||||||
boolean hasPerm = perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
|
return perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
|
||||||
return hasPerm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -59,10 +59,10 @@ mybatis-plus:
|
|||||||
|
|
||||||
# 认证配置
|
# 认证配置
|
||||||
jwt:
|
jwt:
|
||||||
# 签署密钥
|
# 密钥
|
||||||
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
|
key: SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||||
# token 过期时间(单位:秒)
|
# token 过期时间(单位:秒)
|
||||||
expiration: 7200
|
ttl: 7200
|
||||||
|
|
||||||
oss:
|
oss:
|
||||||
# OSS 类型 (目前支持aliyun、minio)
|
# OSS 类型 (目前支持aliyun、minio)
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ mybatis-plus:
|
|||||||
|
|
||||||
# 认证配置
|
# 认证配置
|
||||||
jwt:
|
jwt:
|
||||||
# 签署密钥
|
# 密钥
|
||||||
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789
|
key: SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||||
# token 过期时间(单位:秒)
|
# token 过期时间(单位:秒)
|
||||||
expiration: 7200
|
ttl: 7200
|
||||||
|
|
||||||
oss:
|
oss:
|
||||||
# OSS 类型 (目前支持aliyun、minio)
|
# OSS 类型 (目前支持aliyun、minio)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
||||||
LEFT JOIN sys_role t3 ON t2.role_id = t3.id
|
LEFT JOIN sys_role t3 ON t2.role_id = t3.id
|
||||||
WHERE
|
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
|
ORDER BY t1.sort asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
INNER JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
INNER JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
||||||
INNER JOIN sys_role t3 ON t3.id = t2.role_id
|
INNER JOIN sys_role t3 ON t3.id = t2.role_id
|
||||||
WHERE
|
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
|
AND t1.perm IS NOT NULL
|
||||||
<choose>
|
<choose>
|
||||||
<when test="roles!=null and roles.size()>0">
|
<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_role t2 ON t1.role_id = t2.id AND t2.deleted = 0
|
||||||
INNER JOIN sys_menu t3 ON t1.menu_id = t3.id
|
INNER JOIN sys_menu t3 ON t1.menu_id = t3.id
|
||||||
WHERE
|
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 ''">
|
<if test="roleCode!=null and roleCode.trim() neq ''">
|
||||||
AND t2.`code` = #{roleCode}
|
AND t2.`code` = #{roleCode}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
Reference in New Issue
Block a user