✨ 导入Security模块
This commit is contained in:
parent
0ed91386b6
commit
c3dab7c02a
|
@ -0,0 +1,42 @@
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.auth</groupId>
|
||||||
|
<artifactId>auth-module</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>module-security</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>module-security</name>
|
||||||
|
<description>SpringSecurity模块</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth</groupId>
|
||||||
|
<artifactId>dao-base</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.thymeleaf.extras</groupId>
|
||||||
|
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.auth.module.security.annotation;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PreAuthorize("hasAnyAuthority({auth})")
|
||||||
|
public @interface HasAnyAuthority {
|
||||||
|
String[] auth();
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.auth.module.security.annotation;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PreAuthorize("hasAuthority('USER') || hasAuthority('{value}')")
|
||||||
|
public @interface HasUSERAuthorize {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.auth.module.security.annotation;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前是否是Admin用户
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
public @interface IsAdmin {
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.auth.module.security.annotation.programmatically;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component("auth")
|
||||||
|
public class AuthorizationLogic {
|
||||||
|
|
||||||
|
public boolean decide(String name) {
|
||||||
|
// 直接使用name的实现
|
||||||
|
// System.out.println(name);
|
||||||
|
return name.equalsIgnoreCase("user");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.auth.module.security.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
// @EnableMethodSecurity(prePostEnabled = false)
|
||||||
|
public class AuthorizationManagerConfiguration {
|
||||||
|
|
||||||
|
// @Bean
|
||||||
|
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// Advisor preAuthorize(PreAuthorizationManager manager) {
|
||||||
|
// return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Bean
|
||||||
|
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// Advisor postAuthorize(PostAuthorizationManager manager) {
|
||||||
|
// return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.auth.module.security.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册一个用于Spring Security预授权/后授权的模板元注解默认配置Bean。
|
||||||
|
*
|
||||||
|
* <p>该Bean提供了基于SpEL表达式的权限校验模板,可用于自定义组合注解。</p>
|
||||||
|
*
|
||||||
|
* <h3>典型用法</h3>
|
||||||
|
* <p>通过此配置可以简化自定义权限注解的定义,例如:</p>
|
||||||
|
* <pre>{@code
|
||||||
|
* @Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
* @Retention(RetentionPolicy.RUNTIME)
|
||||||
|
* @PreAuthorize("hasAnyAuthority( // 使用模板提供的表达式语法
|
||||||
|
* public @interface HasAnyAuthority {
|
||||||
|
* String[] auth(); // 接收权限列表参数
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <h3>注意事项</h3>
|
||||||
|
* <ul>
|
||||||
|
* <li>需要确保Spring Security的预授权功能已启用</li>
|
||||||
|
* <li>模板表达式应符合SpEL语法规范</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return PrePostTemplateDefaults 实例,用于预/后授权注解的默认配置
|
||||||
|
* @see org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
* @see org.springframework.security.access.prepost.PostAuthorize
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
PrePostTemplateDefaults prePostTemplateDefaults() {
|
||||||
|
return new PrePostTemplateDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置密码编码器Bean
|
||||||
|
*
|
||||||
|
* <p>Spring Security提供了多种密码编码器实现,推荐使用BCryptPasswordEncoder作为默认选择。</p>
|
||||||
|
*
|
||||||
|
* <p>特点:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>BCryptPasswordEncoder - 使用bcrypt强哈希算法,自动加盐,是当前最推荐的密码编码器</li>
|
||||||
|
* <li>Argon2PasswordEncoder - 使用Argon2算法,抗GPU/ASIC攻击,但需要更多内存</li>
|
||||||
|
* <li>SCryptPasswordEncoder - 使用scrypt算法,内存密集型,抗硬件攻击</li>
|
||||||
|
* <li>Pbkdf2PasswordEncoder - 使用PBKDF2算法,较老但广泛支持</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>注意:不推荐使用MD5等弱哈希算法,Spring官方也不推荐自定义弱密码编码器。</p>
|
||||||
|
*
|
||||||
|
* @return PasswordEncoder 密码编码器实例
|
||||||
|
* @see BCryptPasswordEncoder
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
// 其他编码器示例(根据需求选择一种):
|
||||||
|
// return new Argon2PasswordEncoder(16, 32, 1, 1 << 14, 2);
|
||||||
|
// return new SCryptPasswordEncoder();
|
||||||
|
// return new Pbkdf2PasswordEncoder("secret", 185000, 256);
|
||||||
|
|
||||||
|
// 实际项目中只需返回一个密码编码器
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.auth.module.security.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.auth.module.security.filter.JwtAuthenticationFilter;
|
||||||
|
import com.auth.module.security.handler.SecurityAccessDeniedHandler;
|
||||||
|
import com.auth.module.security.handler.SecurityAuthenticationEntryPoint;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity(jsr250Enabled = true)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SecurityWebConfiguration {
|
||||||
|
|
||||||
|
public static List<String> securedPaths = List.of("/api/**");
|
||||||
|
public static List<String> noAuthPaths = List.of("/*/login");
|
||||||
|
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
|
http
|
||||||
|
// 前端段分离不需要---禁用明文验证
|
||||||
|
.httpBasic(AbstractHttpConfigurer::disable)
|
||||||
|
// 前端段分离不需要---禁用默认登录页
|
||||||
|
.formLogin(AbstractHttpConfigurer::disable)
|
||||||
|
// 前端段分离不需要---禁用退出页
|
||||||
|
.logout(AbstractHttpConfigurer::disable)
|
||||||
|
// 前端段分离不需要---csrf攻击
|
||||||
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
// 跨域访问权限,如果需要可以关闭后自己配置跨域访问
|
||||||
|
.cors(AbstractHttpConfigurer::disable)
|
||||||
|
// 前后端分离不需要---因为是无状态的
|
||||||
|
// .sessionManagement(AbstractHttpConfigurer::disable)
|
||||||
|
.sessionManagement(session ->
|
||||||
|
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests(authorizeRequests ->
|
||||||
|
// 访问路径为 /api 时需要进行认证
|
||||||
|
authorizeRequests
|
||||||
|
// 不认证登录接口
|
||||||
|
.requestMatchers(noAuthPaths.toArray(String[]::new)).permitAll()
|
||||||
|
// ❗只认证 securedPaths 下的所有接口
|
||||||
|
// =======================================================================
|
||||||
|
// 也可以在这里写多参数传入,如:"/api/**","/admin/**"
|
||||||
|
// 但是在 Spring过滤器中,如果要放行不需要认证请求,但是需要认证的接口必需要携带token。
|
||||||
|
// 做法是在这里定义要认证的接口,如果要做成动态可以放到数据库。
|
||||||
|
// =======================================================================
|
||||||
|
.requestMatchers(securedPaths.toArray(String[]::new)).authenticated()
|
||||||
|
// 其余请求都放行
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
)
|
||||||
|
.exceptionHandling(exception -> {
|
||||||
|
// 请求未授权接口
|
||||||
|
exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
|
||||||
|
// 没有权限访问
|
||||||
|
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
|
||||||
|
})
|
||||||
|
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
|
;
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.auth.module.security.event;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AuthenticationEvents {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听拒绝授权内容
|
||||||
|
*
|
||||||
|
* @param failure 授权失败
|
||||||
|
*/
|
||||||
|
@EventListener
|
||||||
|
public void onFailure(AuthorizationDeniedEvent<MethodInvocation> failure) {
|
||||||
|
try {
|
||||||
|
// getSource 和 getObject意思一样,一种是传入泛型自动转换一种是要手动转换
|
||||||
|
Object source = failure.getSource();
|
||||||
|
|
||||||
|
// 直接获取泛型对象
|
||||||
|
MethodInvocation methodInvocation = failure.getObject();
|
||||||
|
Method method = methodInvocation.getMethod();
|
||||||
|
Object[] args = methodInvocation.getArguments();
|
||||||
|
|
||||||
|
log.warn("方法调用被拒绝: {}.{}, 参数: {}",
|
||||||
|
method.getDeclaringClass().getSimpleName(),
|
||||||
|
method.getName(),
|
||||||
|
Arrays.toString(args));
|
||||||
|
|
||||||
|
// 这里面的信息,和接口 /api/security/current-user 内容一样
|
||||||
|
Authentication authentication = failure.getAuthentication().get();
|
||||||
|
|
||||||
|
AuthorizationDecision authorizationDecision = failure.getAuthorizationDecision();
|
||||||
|
// ExpressionAuthorizationDecision [granted=false, expressionAttribute=hasAuthority('ADMIN')]
|
||||||
|
System.out.println(authorizationDecision);
|
||||||
|
|
||||||
|
log.warn("授权失败 - 用户: {}, 权限: {}", authentication.getName(), authorizationDecision);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听授权的内容
|
||||||
|
* 如果要监听授权成功的内容,这个内容可能相当的多,毕竟正常情况授权成功的内容还是比较多的。
|
||||||
|
* 既然内容很多又要监听,如果真的需要,一定要处理好业务逻辑,不要被成功的消息淹没。
|
||||||
|
*
|
||||||
|
* @param success 授权成功
|
||||||
|
*/
|
||||||
|
@EventListener
|
||||||
|
public void onSuccess(AuthorizationGrantedEvent<MethodInvocation> success) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.auth.module.security.event;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
|
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果要监听授权和拒绝的授权需要发布一个像下面这样的事件
|
||||||
|
* 之后使用 Spring 的 @EventListener
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SecurityAuthorizationPublisher {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||||
|
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.auth.module.security.filter;
|
||||||
|
|
||||||
|
import com.auth.common.context.BaseContext;
|
||||||
|
import com.auth.common.exception.AuthenticSecurityException;
|
||||||
|
import com.auth.common.exception.MyAuthenticationException;
|
||||||
|
import com.auth.common.model.common.result.ResultCodeEnum;
|
||||||
|
import com.auth.module.security.config.SecurityWebConfiguration;
|
||||||
|
import com.auth.module.security.handler.SecurityAuthenticationEntryPoint;
|
||||||
|
import com.auth.module.security.service.DbUserDetailService;
|
||||||
|
import com.auth.module.security.service.JwtTokenService;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtTokenService jwtTokenService;
|
||||||
|
private final DbUserDetailService userDetailsService;
|
||||||
|
private final SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException {
|
||||||
|
// 先校验不需要认证的接口
|
||||||
|
RequestMatcher[] requestNoAuthMatchers = SecurityWebConfiguration.noAuthPaths.stream()
|
||||||
|
.map(AntPathRequestMatcher::new)
|
||||||
|
.toArray(RequestMatcher[]::new);
|
||||||
|
OrRequestMatcher noAuthRequestMatcher = new OrRequestMatcher(requestNoAuthMatchers);
|
||||||
|
if (noAuthRequestMatcher.matches(request)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要认证的接口
|
||||||
|
RequestMatcher[] requestSecureMatchers = SecurityWebConfiguration.securedPaths.stream()
|
||||||
|
.map(AntPathRequestMatcher::new)
|
||||||
|
.toArray(RequestMatcher[]::new);
|
||||||
|
OrRequestMatcher secureRequestMatcher = new OrRequestMatcher(requestSecureMatchers);
|
||||||
|
|
||||||
|
// 公开接口直接放行
|
||||||
|
if (!secureRequestMatcher.matches(request)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String authHeader = request.getHeader("Authorization");
|
||||||
|
// 如果当前请求不包含验证Token直接返回
|
||||||
|
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前请求的Token
|
||||||
|
final String jwtToken = authHeader.substring(7);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查当前Token是否过期
|
||||||
|
if (jwtTokenService.isExpired(jwtToken)) {
|
||||||
|
// 💡如果过期不需要进行判断和验证,需要直接放行可以像下面这样写
|
||||||
|
// ===================================================
|
||||||
|
// filterChain.doFilter(request, response);
|
||||||
|
// return;
|
||||||
|
// ===================================================
|
||||||
|
throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析当前Token中的用户名
|
||||||
|
String username = jwtTokenService.getUsernameFromToken(jwtToken);
|
||||||
|
Long userId = jwtTokenService.getUserIdFromToken(jwtToken);
|
||||||
|
|
||||||
|
// 当前用户名存在,并且 Security上下文为空,设置认证相关信息
|
||||||
|
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
// 调用用户信息进行登录
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails,
|
||||||
|
null,
|
||||||
|
userDetails.getAuthorities()
|
||||||
|
);
|
||||||
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
|
||||||
|
// 设置认证用户信息
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
|
BaseContext.setUsername(username);
|
||||||
|
BaseContext.setUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
// ⚠️IMPORTANT:
|
||||||
|
// ==========================================================================
|
||||||
|
// 在 catch 块中,securityAuthenticationEntryPoint.commence() 已经处理了错误响应
|
||||||
|
// 所以应该 直接返回,避免继续执行后续逻辑。
|
||||||
|
// ==========================================================================
|
||||||
|
catch (RuntimeException exception) {
|
||||||
|
securityAuthenticationEntryPoint.commence(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
new MyAuthenticationException(exception.getMessage(), exception)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.auth.module.security.handler;
|
||||||
|
|
||||||
|
import com.auth.common.model.common.result.Result;
|
||||||
|
import com.auth.common.model.common.result.ResultCodeEnum;
|
||||||
|
import com.auth.common.utils.ResponseUtil;
|
||||||
|
import com.auth.module.security.service.JwtTokenService;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现注销处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtTokenLogoutHandler implements LogoutHandler {
|
||||||
|
|
||||||
|
private final JwtTokenService jwtTokenService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
|
// 在这里可以设置不同的请求头标识符,常见的:Authorization、Token等
|
||||||
|
String authorizationToken = request.getHeader("Authorization");
|
||||||
|
|
||||||
|
if (StringUtils.hasText(authorizationToken)) {
|
||||||
|
// 如果当前用户信息存在redis中可以通过这个进行退出
|
||||||
|
String username = jwtTokenService.getUsernameFromToken(authorizationToken);
|
||||||
|
log.info("username : {}", username);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Object> result = Result.success(ResultCodeEnum.SUCCESS_LOGOUT);
|
||||||
|
ResponseUtil.out(response, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.auth.module.security.handler;
|
||||||
|
|
||||||
|
import com.auth.common.model.common.result.Result;
|
||||||
|
import com.auth.common.model.common.result.ResultCodeEnum;
|
||||||
|
import com.auth.common.utils.ResponseUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {
|
||||||
|
log.error("SecurityAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage());
|
||||||
|
|
||||||
|
// 无权访问接口
|
||||||
|
Result<Object> result = Result.error(accessDeniedException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
|
||||||
|
ResponseUtil.out(response, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.auth.module.security.handler;
|
||||||
|
|
||||||
|
import com.auth.common.exception.MyAuthenticationException;
|
||||||
|
import com.auth.common.model.common.result.Result;
|
||||||
|
import com.auth.common.model.common.result.ResultCodeEnum;
|
||||||
|
import com.auth.common.utils.ResponseUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
|
||||||
|
log.error("SecurityAuthenticationEntryPoint:{}", authException.getLocalizedMessage());
|
||||||
|
Result<Object> result;
|
||||||
|
|
||||||
|
// 自定义认证异常
|
||||||
|
if (authException instanceof MyAuthenticationException) {
|
||||||
|
result = Result.error(null, authException.getMessage());
|
||||||
|
ResponseUtil.out(response, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未认证---未登录
|
||||||
|
result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
|
||||||
|
ResponseUtil.out(response, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.auth.module.security.manger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理方法调用后的授权检查
|
||||||
|
* check()方法接收的是MethodInvocationResult对象,包含已执行方法的结果
|
||||||
|
* 用于决定是否允许返回某个方法的结果(后置过滤)
|
||||||
|
* 这是Spring Security较新的"后置授权"功能
|
||||||
|
*/
|
||||||
|
// @Component
|
||||||
|
// public class PostAuthorizationManager implements AuthorizationManager<MethodInvocationResult> {
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 这里两个实现方法按照Security官方要求进行实现
|
||||||
|
// * <h4>类说明:</h4>
|
||||||
|
// * 下面的实现是对方法执行前进行权限校验的判断
|
||||||
|
// * <pre>
|
||||||
|
// * <code>AuthorizationManager <MethodInvocation></code>
|
||||||
|
// * </pre>
|
||||||
|
// * 下面的这个是对方法执行后对权限的判断
|
||||||
|
// * <pre>
|
||||||
|
// * <code>AuthorizationManager <MethodInvocationResult></code>
|
||||||
|
// * </pre>
|
||||||
|
// *
|
||||||
|
// * <h4>注意事项:</h4>
|
||||||
|
// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。
|
||||||
|
// * <pre>
|
||||||
|
// * @Configuration
|
||||||
|
// * @EnableMethodSecurity(prePostEnabled = false)
|
||||||
|
// * class MethodSecurityConfig {
|
||||||
|
// * @Bean
|
||||||
|
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// * Advisor preAuthorize(MyAuthorizationManager manager) {
|
||||||
|
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
|
||||||
|
// * }
|
||||||
|
// *
|
||||||
|
// * @Bean
|
||||||
|
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// * Advisor postAuthorize(MyAuthorizationManager manager) {
|
||||||
|
// * return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
|
||||||
|
// * }
|
||||||
|
// * }
|
||||||
|
// * </pre>
|
||||||
|
// */
|
||||||
|
// @Override
|
||||||
|
// public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult invocation) {
|
||||||
|
// return new AuthorizationDecision(true);
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.auth.module.security.manger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理方法调用前的授权检查
|
||||||
|
* check()方法接收的是MethodInvocation对象,包含即将执行的方法调用信息
|
||||||
|
* 用于决定是否允许执行某个方法
|
||||||
|
* 这是传统的"前置授权"模式
|
||||||
|
*/
|
||||||
|
// @Component
|
||||||
|
// public class PreAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 这里两个实现方法按照Security官方要求进行实现
|
||||||
|
// * <h4>类说明:</h4>
|
||||||
|
// * 下面的实现是对方法执行前进行权限校验的判断
|
||||||
|
// * <pre>
|
||||||
|
// * <code>AuthorizationManager <MethodInvocation></code>
|
||||||
|
// * </pre>
|
||||||
|
// * 下面的这个是对方法执行后对权限的判断
|
||||||
|
// * <pre>
|
||||||
|
// * <code>AuthorizationManager <MethodInvocationResult></code>
|
||||||
|
// * </pre>
|
||||||
|
// *
|
||||||
|
// * <h4>注意事项:</h4>
|
||||||
|
// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。
|
||||||
|
// * <pre>
|
||||||
|
// * @Configuration
|
||||||
|
// * @EnableMethodSecurity(prePostEnabled = false)
|
||||||
|
// * class MethodSecurityConfig {
|
||||||
|
// * @Bean
|
||||||
|
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// * Advisor preAuthorize(MyAuthorizationManager manager) {
|
||||||
|
// * return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
|
||||||
|
// * }
|
||||||
|
// *
|
||||||
|
// * @Bean
|
||||||
|
// * @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
// * Advisor postAuthorize(MyAuthorizationManager manager) {
|
||||||
|
// * return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
|
||||||
|
// * }
|
||||||
|
// * }
|
||||||
|
// * </pre>
|
||||||
|
// */
|
||||||
|
// @Override
|
||||||
|
// public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
|
||||||
|
// return new AuthorizationDecision(true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
|
@ -0,0 +1 @@
|
||||||
|
如果需要重写验证逻辑(自定义)使用这里面的类,并在配置类`AuthorizationManagerConfiguration`解开注释,
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.auth.module.security.password;
|
||||||
|
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>MD5密码编码器实现</h1>
|
||||||
|
*
|
||||||
|
* <strong>安全警告:</strong>此类使用MD5算法进行密码哈希,已不再安全,不推荐用于生产环境。
|
||||||
|
*
|
||||||
|
* <p>MD5算法因其计算速度快且易受彩虹表攻击而被认为不安全。即使密码哈希本身是单向的,
|
||||||
|
* 但现代计算能力使得暴力破解和预先计算的彩虹表攻击变得可行。</p>
|
||||||
|
*
|
||||||
|
* <p>Spring Security推荐使用BCrypt、PBKDF2、Argon2或Scrypt等自适应单向函数替代MD5。</p>
|
||||||
|
*
|
||||||
|
* @see PasswordEncoder
|
||||||
|
* 一般仅用于遗留系统兼容,新系统应使用更安全的密码编码器
|
||||||
|
*/
|
||||||
|
public class MD5PasswordEncoder implements PasswordEncoder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encode(CharSequence rawPassword) {
|
||||||
|
if (rawPassword == null) {
|
||||||
|
throw new IllegalArgumentException("原始密码不能为null");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] md5Digest = DigestUtils.md5Digest(rawPassword.toString().getBytes());
|
||||||
|
return HexFormat.of().formatHex(md5Digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||||
|
if (rawPassword == null) {
|
||||||
|
throw new IllegalArgumentException("原始密码不能为null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(encodedPassword)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodedPassword.equalsIgnoreCase(encode(rawPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean upgradeEncoding(String encodedPassword) {
|
||||||
|
// MD5已不安全,始终返回true建议升级到更安全的算法
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.auth.module.security.service;
|
||||||
|
|
||||||
|
import com.auth.common.model.entity.base.PermissionEntity;
|
||||||
|
import com.auth.common.model.entity.base.RoleEntity;
|
||||||
|
import com.auth.common.model.entity.base.UserEntity;
|
||||||
|
import com.auth.dao.base.mapper.v1.UserMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DbUserDetailService implements UserDetailsService {
|
||||||
|
|
||||||
|
// TODO 找不到UserMapper
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
// 查询当前用户
|
||||||
|
UserEntity userEntity = userMapper.selectByUsername(username);
|
||||||
|
|
||||||
|
// 判断当前用户是否存在
|
||||||
|
if (userEntity == null) {
|
||||||
|
throw new UsernameNotFoundException("用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long userId = userEntity.getId();
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
// 设置用户角色
|
||||||
|
List<String> roles = findUserRolesByUserId(userId);
|
||||||
|
// 设置用户权限
|
||||||
|
List<String> permissions = findPermissionByUserId(userId);
|
||||||
|
list.addAll(roles);
|
||||||
|
list.addAll(permissions);
|
||||||
|
|
||||||
|
Set<SimpleGrantedAuthority> authorities = list.stream().map(SimpleGrantedAuthority::new)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 设置用户权限
|
||||||
|
userEntity.setAuthorities(authorities);
|
||||||
|
// 返回时将用户密码置为空
|
||||||
|
userEntity.setPassword(null);
|
||||||
|
return userEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户id查找该用户的角色内容
|
||||||
|
*
|
||||||
|
* @param userId 用户id
|
||||||
|
* @return 当前用户的角色信息
|
||||||
|
*/
|
||||||
|
public List<String> findUserRolesByUserId(Long userId) {
|
||||||
|
List<RoleEntity> roleList = userMapper.selectRolesByUserId(userId);
|
||||||
|
return roleList.stream().map(RoleEntity::getRoleCode).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户id查找该用户的权限内容
|
||||||
|
*
|
||||||
|
* @param userId 用户id
|
||||||
|
* @return 当前用户的权限信息
|
||||||
|
*/
|
||||||
|
public List<String> findPermissionByUserId(Long userId) {
|
||||||
|
List<PermissionEntity> permissionList = userMapper.selectPermissionByUserId(userId);
|
||||||
|
return permissionList.stream().map(PermissionEntity::getPermissionCode).toList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.auth.module.security.service;
|
||||||
|
|
||||||
|
// @Service
|
||||||
|
// @RequiredArgsConstructor
|
||||||
|
// public class InMemoryUserDetailsService implements UserDetailsService {
|
||||||
|
//
|
||||||
|
// private final PasswordEncoder passwordEncoder;
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
// // 1. 这里应该根据username从数据库或其他存储中查询用户信息
|
||||||
|
// // 以下是模拟数据,实际应用中应从数据库查询
|
||||||
|
//
|
||||||
|
// // 2. 如果用户不存在,抛出UsernameNotFoundException
|
||||||
|
// if (!"bunny".equalsIgnoreCase(username)) {
|
||||||
|
// throw new UsernameNotFoundException("User not found: " + username);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 3. 构建UserDetails对象返回
|
||||||
|
// return User.builder()
|
||||||
|
// .username(username) // 使用传入的用户名
|
||||||
|
// .password(passwordEncoder.encode("123456")) // 密码应该已经加密存储,这里仅为示例
|
||||||
|
// .roles("USER") // 角色会自动添加ROLE_前缀
|
||||||
|
// .authorities("read", "write") // 添加具体权限
|
||||||
|
// .build();
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.auth.module.security.service;
|
||||||
|
|
||||||
|
import com.auth.common.utils.JwtTokenUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "jwt-token")
|
||||||
|
public class JwtTokenService {
|
||||||
|
|
||||||
|
@Value("${jwtToken.secret}")
|
||||||
|
public String secret;
|
||||||
|
|
||||||
|
@Value("${jwtToken.subject}")
|
||||||
|
public String subject;
|
||||||
|
|
||||||
|
// private final SecretKey securityKey = Keys.hmacShaKeyFor("Bunny-Auth-Server-Private-SecretKey".getBytes(StandardCharsets.UTF_8));
|
||||||
|
// JWT 的 秘钥
|
||||||
|
@Value("${jwtToken.expired}")
|
||||||
|
public Long expired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Token
|
||||||
|
*
|
||||||
|
* @param userId 用户Id
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 令牌Token
|
||||||
|
*/
|
||||||
|
public String createToken(Long userId, String username,
|
||||||
|
List<String> roles, List<String> permissions) {
|
||||||
|
SecretKey key = getSecretKey();
|
||||||
|
// return JwtTokenUtil.createToken(userId, username, subject, key, expired);
|
||||||
|
return JwtTokenUtil.createToken(userId, username, roles, permissions, subject, key, expired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取安全密钥
|
||||||
|
*
|
||||||
|
* @return {@link SecretKey}
|
||||||
|
*/
|
||||||
|
private SecretKey getSecretKey() {
|
||||||
|
byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
|
||||||
|
return new SecretKeySpec(secretBytes, "HmacSHA256");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据Token获取用户名
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户名
|
||||||
|
*/
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
SecretKey secretKey = getSecretKey();
|
||||||
|
return JwtTokenUtil.getUsername(token, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据Token获取用户Id
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户Id
|
||||||
|
*/
|
||||||
|
public Long getUserIdFromToken(String token) {
|
||||||
|
SecretKey secretKey = getSecretKey();
|
||||||
|
return JwtTokenUtil.getUserId(token, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据Token获取用户Id
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户Id
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getMapByToken(String token) {
|
||||||
|
SecretKey secretKey = getSecretKey();
|
||||||
|
return JwtTokenUtil.getMapByToken(token, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前Token是否国企
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 是否国企
|
||||||
|
*/
|
||||||
|
public boolean isExpired(String token) {
|
||||||
|
SecretKey secretKey = getSecretKey();
|
||||||
|
return JwtTokenUtil.isExpired(token, secretKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
jwtToken:
|
||||||
|
# 密钥
|
||||||
|
secret: aVeryLongAndSecureRandomStringWithAtLeast32Characters
|
||||||
|
# 主题
|
||||||
|
subject: SecurityBunny
|
||||||
|
# 过期事件 7天
|
||||||
|
expired: 604800
|
Loading…
Reference in New Issue