✨ 实现后端校验登录
This commit is contained in:
parent
02901efa33
commit
20e7f71936
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,14 @@ public class GlobalExceptionHandler {
|
||||||
public Result<Object> exceptionHandler(RuntimeException exception) {
|
public Result<Object> exceptionHandler(RuntimeException exception) {
|
||||||
String message = exception.getMessage();
|
String message = exception.getMessage();
|
||||||
message = StringUtils.hasText(message) ? message : "服务器异常";
|
message = StringUtils.hasText(message) ? message : "服务器异常";
|
||||||
exception.printStackTrace();
|
log.error("发生业务异常: {}", exception.getMessage(), exception);
|
||||||
|
|
||||||
|
// 💡IDEA:如果需要特殊情况的日志可以参考下面的代码
|
||||||
|
// =========================================
|
||||||
|
// StringWriter sw = new StringWriter();
|
||||||
|
// e.printStackTrace(new PrintWriter(sw));
|
||||||
|
// logger.error(sw.toString());
|
||||||
|
// =========================================
|
||||||
|
|
||||||
// 解析异常
|
// 解析异常
|
||||||
String jsonParseError = "JSON parse error (.*)";
|
String jsonParseError = "JSON parse error (.*)";
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.spring.step3.exception;
|
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义未认证异常
|
||||||
|
*/
|
||||||
|
public class MyAuthenticationException extends AuthenticationException {
|
||||||
|
/**
|
||||||
|
* Constructs an {@code AuthenticationException} with the specified message and root
|
||||||
|
* cause.
|
||||||
|
*
|
||||||
|
* @param msg the detail message
|
||||||
|
* @param cause the root cause
|
||||||
|
*/
|
||||||
|
public MyAuthenticationException(String msg, Throwable cause) {
|
||||||
|
super(msg, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ import org.springframework.stereotype.Component;
|
||||||
public class AuthorizationLogic {
|
public class AuthorizationLogic {
|
||||||
|
|
||||||
public boolean decide(String name) {
|
public boolean decide(String name) {
|
||||||
System.out.println(name);
|
|
||||||
// 直接使用name的实现
|
// 直接使用name的实现
|
||||||
|
// System.out.println(name);
|
||||||
return name.equalsIgnoreCase("user");
|
return name.equalsIgnoreCase("user");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,21 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableMethodSecurity(jsr250Enabled = true)
|
@EnableMethodSecurity(jsr250Enabled = true)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SecurityWebConfiguration {
|
public class SecurityWebConfiguration {
|
||||||
|
|
||||||
|
public static List<String> securedPaths = List.of("/api/**");
|
||||||
|
public static List<String> noAuthPaths = List.of("/*/login");
|
||||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
http
|
http
|
||||||
// 前端段分离不需要---禁用明文验证
|
// 前端段分离不需要---禁用明文验证
|
||||||
.httpBasic(AbstractHttpConfigurer::disable)
|
.httpBasic(AbstractHttpConfigurer::disable)
|
||||||
|
@ -44,9 +49,14 @@ public class SecurityWebConfiguration {
|
||||||
// 访问路径为 /api 时需要进行认证
|
// 访问路径为 /api 时需要进行认证
|
||||||
authorizeRequests
|
authorizeRequests
|
||||||
// 不认证登录接口
|
// 不认证登录接口
|
||||||
.requestMatchers("/*/login", "/api/security/**").permitAll()
|
.requestMatchers(noAuthPaths.toArray(String[]::new)).permitAll()
|
||||||
// 只认证 /api/** 下的所有接口
|
// ❗只认证 securedPaths 下的所有接口
|
||||||
.requestMatchers("/api/**").authenticated()
|
// =======================================================================
|
||||||
|
// 也可以在这里写多参数传入,如:"/api/**","/admin/**"
|
||||||
|
// 但是在 Spring过滤器中,如果要放行不需要认证请求,但是需要认证的接口必需要携带token。
|
||||||
|
// 做法是在这里定义要认证的接口,如果要做成动态可以放到数据库。
|
||||||
|
// =======================================================================
|
||||||
|
.requestMatchers(securedPaths.toArray(String[]::new)).authenticated()
|
||||||
// 其余请求都放行
|
// 其余请求都放行
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
)
|
)
|
||||||
|
@ -56,7 +66,7 @@ public class SecurityWebConfiguration {
|
||||||
// 没有权限访问
|
// 没有权限访问
|
||||||
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
|
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
|
||||||
})
|
})
|
||||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
.addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||||
;
|
;
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
|
|
|
@ -3,8 +3,11 @@ package com.spring.step3.security.filter;
|
||||||
import com.spring.step3.config.context.BaseContext;
|
import com.spring.step3.config.context.BaseContext;
|
||||||
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
||||||
import com.spring.step3.exception.AuthenticSecurityException;
|
import com.spring.step3.exception.AuthenticSecurityException;
|
||||||
|
import com.spring.step3.exception.MyAuthenticationException;
|
||||||
|
import com.spring.step3.security.config.SecurityWebConfiguration;
|
||||||
|
import com.spring.step3.security.handler.SecurityAuthenticationEntryPoint;
|
||||||
import com.spring.step3.security.service.DbUserDetailService;
|
import com.spring.step3.security.service.DbUserDetailService;
|
||||||
import com.spring.step3.security.service.JwtBearTokenService;
|
import com.spring.step3.security.service.JwtTokenService;
|
||||||
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;
|
||||||
|
@ -16,6 +19,9 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
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.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
@ -26,52 +32,91 @@ import java.io.IOException;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final JwtBearTokenService jwtBearTokenService;
|
private final JwtTokenService jwtTokenService;
|
||||||
private final DbUserDetailService userDetailsService;
|
private final DbUserDetailService userDetailsService;
|
||||||
|
private final SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(@NotNull HttpServletRequest request,
|
protected void doFilterInternal(@NotNull HttpServletRequest request,
|
||||||
@NotNull HttpServletResponse response,
|
@NotNull HttpServletResponse response,
|
||||||
@NotNull FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException {
|
@NotNull FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException {
|
||||||
final String authHeader = request.getHeader("Authorization");
|
// 先校验不需要认证的接口
|
||||||
|
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直接返回
|
// 如果当前请求不包含验证Token直接返回
|
||||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
// throw new SecurityException(ResultCodeEnum.LOGIN_AUTH);
|
throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前请求的Token
|
// 当前请求的Token
|
||||||
final String jwtToken = authHeader.substring(7);
|
final String jwtToken = authHeader.substring(7);
|
||||||
|
|
||||||
// 检查当前Token是否过期
|
try {
|
||||||
if (jwtBearTokenService.isTokenValid(jwtToken)) {
|
// 检查当前Token是否过期
|
||||||
// TODO 抛出异常 Security 未处理
|
if (jwtTokenService.isExpired(jwtToken)) {
|
||||||
throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
|
// 💡如果过期不需要进行判断和验证,需要直接放行可以像下面这样写
|
||||||
|
// ===================================================
|
||||||
|
// 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:
|
||||||
// 解析当前Token中的用户名
|
// ==========================================================================
|
||||||
final String username = jwtBearTokenService.getUsernameFromToken(jwtToken);
|
// 在 catch 块中,securityAuthenticationEntryPoint.commence() 已经处理了错误响应
|
||||||
final Long userId = jwtBearTokenService.getUserIdFromToken(jwtToken);
|
// 所以应该 直接返回,避免继续执行后续逻辑。
|
||||||
|
// ==========================================================================
|
||||||
// 当前用户名存在,并且 Security上下文为空,设置认证相关信息
|
catch (RuntimeException exception) {
|
||||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
securityAuthenticationEntryPoint.commence(
|
||||||
// 调用用户信息进行登录
|
request,
|
||||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
response,
|
||||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
new MyAuthenticationException(exception.getMessage(), exception)
|
||||||
userDetails,
|
|
||||||
null,
|
|
||||||
userDetails.getAuthorities()
|
|
||||||
);
|
);
|
||||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
|
|
||||||
// 设置认证用户信息
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
|
||||||
BaseContext.setUsername(username);
|
|
||||||
BaseContext.setUserId(userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.spring.step3.security.handler;
|
||||||
|
|
||||||
import com.spring.step3.domain.vo.result.Result;
|
import com.spring.step3.domain.vo.result.Result;
|
||||||
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
||||||
import com.spring.step3.security.service.JwtBearTokenService;
|
import com.spring.step3.security.service.JwtTokenService;
|
||||||
import com.spring.step3.utils.ResponseUtil;
|
import com.spring.step3.utils.ResponseUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
@ -21,14 +21,16 @@ import org.springframework.util.StringUtils;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class JwtTokenLogoutHandler implements LogoutHandler {
|
public class JwtTokenLogoutHandler implements LogoutHandler {
|
||||||
|
|
||||||
private final JwtBearTokenService jwtBearTokenService;
|
private final JwtTokenService jwtTokenService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
|
// 在这里可以设置不同的请求头标识符,常见的:Authorization、Token等
|
||||||
String authorizationToken = request.getHeader("Authorization");
|
String authorizationToken = request.getHeader("Authorization");
|
||||||
|
|
||||||
if (StringUtils.hasText(authorizationToken)) {
|
if (StringUtils.hasText(authorizationToken)) {
|
||||||
// 如果当前用户信息存在redis中可以通过这个进行退出
|
// 如果当前用户信息存在redis中可以通过这个进行退出
|
||||||
String username = jwtBearTokenService.getUsernameFromToken(authorizationToken);
|
String username = jwtTokenService.getUsernameFromToken(authorizationToken);
|
||||||
log.info("username : {}", username);
|
log.info("username : {}", username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,32 @@ package com.spring.step3.security.handler;
|
||||||
|
|
||||||
import com.spring.step3.domain.vo.result.Result;
|
import com.spring.step3.domain.vo.result.Result;
|
||||||
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
||||||
|
import com.spring.step3.exception.MyAuthenticationException;
|
||||||
import com.spring.step3.utils.ResponseUtil;
|
import com.spring.step3.utils.ResponseUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@Component
|
||||||
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
|
||||||
log.error("SecurityAuthenticationEntryPoint:{}", authException.getLocalizedMessage());
|
log.error("SecurityAuthenticationEntryPoint:{}", authException.getLocalizedMessage());
|
||||||
|
Result<Object> result;
|
||||||
|
|
||||||
|
// 自定义认证异常
|
||||||
|
if (authException instanceof MyAuthenticationException) {
|
||||||
|
result = Result.error(null, authException.getMessage());
|
||||||
|
ResponseUtil.out(response, result);
|
||||||
|
}
|
||||||
|
|
||||||
// 未认证---未登录
|
// 未认证---未登录
|
||||||
Result<Object> result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
|
result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
|
||||||
ResponseUtil.out(response, result);
|
ResponseUtil.out(response, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.Map;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "jwt-token")
|
@ConfigurationProperties(prefix = "jwt-token")
|
||||||
public class JwtBearTokenService {
|
public class JwtTokenService {
|
||||||
|
|
||||||
@Value("${jwtToken.secret}")
|
@Value("${jwtToken.secret}")
|
||||||
public String secret;
|
public String secret;
|
||||||
|
@ -89,7 +89,7 @@ public class JwtBearTokenService {
|
||||||
* @param token 令牌
|
* @param token 令牌
|
||||||
* @return 是否国企
|
* @return 是否国企
|
||||||
*/
|
*/
|
||||||
public boolean isTokenValid(String token) {
|
public boolean isExpired(String token) {
|
||||||
SecretKey secretKey = getSecretKey();
|
SecretKey secretKey = getSecretKey();
|
||||||
return JwtTokenUtil.isExpired(token, secretKey);
|
return JwtTokenUtil.isExpired(token, secretKey);
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ import com.spring.step3.domain.vo.LoginVo;
|
||||||
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
import com.spring.step3.domain.vo.result.ResultCodeEnum;
|
||||||
import com.spring.step3.mapper.UserMapper;
|
import com.spring.step3.mapper.UserMapper;
|
||||||
import com.spring.step3.security.service.DbUserDetailService;
|
import com.spring.step3.security.service.DbUserDetailService;
|
||||||
import com.spring.step3.security.service.JwtBearTokenService;
|
import com.spring.step3.security.service.JwtTokenService;
|
||||||
import com.spring.step3.service.user.LoginService;
|
import com.spring.step3.service.user.LoginService;
|
||||||
import com.spring.step3.service.user.strategy.DefaultLoginStrategy;
|
import com.spring.step3.service.user.strategy.DefaultLoginStrategy;
|
||||||
import com.spring.step3.service.user.strategy.LoginContext;
|
import com.spring.step3.service.user.strategy.LoginContext;
|
||||||
|
@ -30,7 +30,7 @@ import java.util.List;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoginServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements LoginService {
|
public class LoginServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements LoginService {
|
||||||
|
|
||||||
private final JwtBearTokenService jwtBearTokenService;
|
private final JwtTokenService jwtTokenService;
|
||||||
private final DbUserDetailService dbUserDetailService;
|
private final DbUserDetailService dbUserDetailService;
|
||||||
private final UserMapper userMapper;
|
private final UserMapper userMapper;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
@ -73,10 +73,10 @@ public class LoginServiceImpl extends ServiceImpl<UserMapper, UserEntity> implem
|
||||||
|
|
||||||
List<String> roles = dbUserDetailService.findUserRolesByUserId(userId);
|
List<String> roles = dbUserDetailService.findUserRolesByUserId(userId);
|
||||||
List<String> permission = dbUserDetailService.findPermissionByUserId(userId);
|
List<String> permission = dbUserDetailService.findPermissionByUserId(userId);
|
||||||
String token = jwtBearTokenService.createToken(userId, user.getUsername(), roles, permission);
|
String token = jwtTokenService.createToken(userId, user.getUsername(), roles, permission);
|
||||||
|
|
||||||
// 过期时间
|
// 过期时间
|
||||||
Long expiresInSeconds = jwtBearTokenService.expired;
|
Long expiresInSeconds = jwtTokenService.expired;
|
||||||
long expirationMillis = System.currentTimeMillis() + (expiresInSeconds * 1000);
|
long expirationMillis = System.currentTimeMillis() + (expiresInSeconds * 1000);
|
||||||
Date date = new Date(expirationMillis);
|
Date date = new Date(expirationMillis);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -95,15 +98,23 @@ public class JwtTokenUtil {
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
* @param username 用户名
|
* @param username 用户名
|
||||||
* @param day 过期时间
|
* @param second 过期时间
|
||||||
* @return token值
|
* @return token值
|
||||||
*/
|
*/
|
||||||
public static String createToken(Long userId, String username,
|
public static String createToken(Long userId, String username,
|
||||||
List<String> roles, List<String> permissions,
|
List<String> roles, List<String> permissions,
|
||||||
String subject, SecretKey key, Long day) {
|
String subject, SecretKey key, Long second) {
|
||||||
|
// 传进来的是秒,转成未来过期时间
|
||||||
|
LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(second);
|
||||||
|
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
|
||||||
|
// 转成过期时间
|
||||||
|
String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.subject(subject)
|
.subject(subject)
|
||||||
.expiration(new Date(System.currentTimeMillis() + day))
|
.expiration(date)
|
||||||
|
.claim("expiredTime", format)
|
||||||
.claim("userId", userId)
|
.claim("userId", userId)
|
||||||
.claim("username", username)
|
.claim("username", username)
|
||||||
.claim("roles", roles)
|
.claim("roles", roles)
|
||||||
|
@ -197,6 +208,7 @@ public class JwtTokenUtil {
|
||||||
*
|
*
|
||||||
* @param token token
|
* @param token token
|
||||||
* @return 是否过期
|
* @return 是否过期
|
||||||
|
* @throws RuntimeException ⚠️解析失败和过期都属于异常类型
|
||||||
*/
|
*/
|
||||||
public static boolean isExpired(String token, SecretKey key) {
|
public static boolean isExpired(String token, SecretKey key) {
|
||||||
try {
|
try {
|
||||||
|
@ -204,9 +216,10 @@ public class JwtTokenUtil {
|
||||||
Date expiration = claimsJws.getPayload().getExpiration();
|
Date expiration = claimsJws.getPayload().getExpiration();
|
||||||
|
|
||||||
return expiration != null && expiration.before(new Date());
|
return expiration != null && expiration.before(new Date());
|
||||||
} catch (Exception exception) {
|
} catch (RuntimeException exception) {
|
||||||
// TODO 抛出异常 Security 未处理
|
// ResultCodeEnum codeEnum = ResultCodeEnum.AUTHENTICATION_EXPIRED;
|
||||||
throw new AuthenticSecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
|
// throw new IllegalArgumentException(codeEnum.getMessage(), exception);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.spring.step2.mapper.UserRoleMapper">
|
<mapper namespace="com.spring.step3.mapper.UserRoleMapper">
|
||||||
|
|
||||||
<!-- 通用查询映射结果 -->
|
<!-- 通用查询映射结果 -->
|
||||||
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.UserRoleEntity">
|
<resultMap id="BaseResultMap" type="com.spring.step3.domain.entity.UserRoleEntity">
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
||||||
|
package com.spring.step3.security.service;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
class JwtTokenServiceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createToken() {
|
||||||
|
byte[] secretBytes = "aVeryLongAndSecureRandomStringWithAtLeast32Characters".getBytes(StandardCharsets.UTF_8);
|
||||||
|
SecretKeySpec hmacSHA256 = new SecretKeySpec(secretBytes, "HmacSHA256");
|
||||||
|
|
||||||
|
String token = Jwts.builder()
|
||||||
|
.subject("")
|
||||||
|
.expiration(new Date(System.currentTimeMillis() - 60 * 60 * 60 * 24 * 7))
|
||||||
|
|
||||||
|
.id(UUID.randomUUID().toString())
|
||||||
|
.signWith(hmacSHA256)
|
||||||
|
.compressWith(Jwts.ZIP.GZIP).compact();
|
||||||
|
|
||||||
|
System.out.println(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue