增加登录的异常,修复重复调用error
This commit is contained in:
parent
e3aa3d1aca
commit
527b7563c0
@ -32,7 +32,12 @@ public enum ResponseCode {
|
||||
DEPENDENCY_INJECTION_SERVICE_NOT_FOUND(1100, "dependency.injection.service.not.found"),
|
||||
DEPENDENCY_INJECTION_REPOSITORY_NOT_FOUND(1101, "dependency.injection.repository.not.found"),
|
||||
DEPENDENCY_INJECTION_CONVERTER_NOT_FOUND(1102, "dependency.injection.converter.not.found"),
|
||||
DEPENDENCY_INJECTION_ENTITYPATH_FAILED(1103, "dependency.injection.entitypath.failed");
|
||||
DEPENDENCY_INJECTION_ENTITYPATH_FAILED(1103, "dependency.injection.entitypath.failed"),
|
||||
|
||||
// JWT相关错误码 (2100-2199)
|
||||
JWT_EXPIRED(2100, "jwt.token.expired"),
|
||||
JWT_INVALID(2101, "jwt.token.invalid"),
|
||||
JWT_MISSING(2102, "jwt.token.missing");
|
||||
|
||||
private final int code;
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ package com.qqchen.deploy.backend.framework.exception;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.api.Response;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
@ -9,6 +11,8 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.security.SignatureException;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ -37,4 +41,16 @@ public class GlobalExceptionHandler {
|
||||
log.error("Unexpected error occurred", e);
|
||||
return Response.error(ResponseCode.ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler(ExpiredJwtException.class)
|
||||
public Response<?> handleExpiredJwtException(ExpiredJwtException e) {
|
||||
log.warn("JWT token expired", e);
|
||||
return Response.error(ResponseCode.JWT_EXPIRED);
|
||||
}
|
||||
|
||||
@ExceptionHandler({SignatureException.class, MalformedJwtException.class})
|
||||
public Response<?> handleInvalidJwtException(Exception e) {
|
||||
log.warn("Invalid JWT token", e);
|
||||
return Response.error(ResponseCode.JWT_INVALID);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.qqchen.deploy.backend.framework.exception;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
|
||||
/**
|
||||
* JWT认证异常
|
||||
*/
|
||||
public class JwtAuthenticationException extends BusinessException {
|
||||
|
||||
public JwtAuthenticationException(ResponseCode errorCode) {
|
||||
super(errorCode);
|
||||
}
|
||||
|
||||
public JwtAuthenticationException(ResponseCode errorCode, Object[] args) {
|
||||
super(errorCode, args);
|
||||
}
|
||||
|
||||
public static JwtAuthenticationException expired() {
|
||||
return new JwtAuthenticationException(ResponseCode.JWT_EXPIRED);
|
||||
}
|
||||
|
||||
public static JwtAuthenticationException invalid() {
|
||||
return new JwtAuthenticationException(ResponseCode.JWT_INVALID);
|
||||
}
|
||||
|
||||
public static JwtAuthenticationException missing() {
|
||||
return new JwtAuthenticationException(ResponseCode.JWT_MISSING);
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,32 @@
|
||||
package com.qqchen.deploy.backend.framework.security.filter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.framework.api.Response;
|
||||
import com.qqchen.deploy.backend.framework.context.TenantContext;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.framework.exception.JwtAuthenticationException;
|
||||
import com.qqchen.deploy.backend.framework.exception.SystemException;
|
||||
import com.qqchen.deploy.backend.framework.security.util.JwtTokenUtil;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@ -43,37 +56,58 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
|
||||
try {
|
||||
// String tenantId = request.getHeader("X-Tenant-ID");
|
||||
// if (tenantId != null && !tenantId.isEmpty()) {
|
||||
// TenantContext.setTenantId(tenantId);
|
||||
// }
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
String jwt = getJwtFromRequest(request);
|
||||
if (StringUtils.hasText(jwt)) {
|
||||
String username = jwtTokenUtil.getUsernameFromToken(jwt);
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
String username = jwtTokenUtil.getUsernameFromToken(token);
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
|
||||
if (jwtTokenUtil.validateToken(token, userDetails)) {
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
if (jwtTokenUtil.validateToken(jwt, userDetails)) {
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
} catch (ExpiredJwtException e) {
|
||||
handleAuthenticationException(response, JwtAuthenticationException.expired());
|
||||
} catch (SignatureException | MalformedJwtException e) {
|
||||
handleAuthenticationException(response, JwtAuthenticationException.invalid());
|
||||
} catch (Exception e) {
|
||||
log.error("JWT authentication failed", e);
|
||||
SecurityContextHolder.clearContext();
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
handleAuthenticationException(response, new SystemException("JWT authentication failed", e));
|
||||
} finally {
|
||||
TenantContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAuthenticationException(HttpServletResponse response, Exception e) {
|
||||
try {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
|
||||
Response<?> errorResponse;
|
||||
if (e instanceof BusinessException be) {
|
||||
errorResponse = Response.error(be.getErrorCode());
|
||||
} else {
|
||||
errorResponse = Response.error(ResponseCode.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
new ObjectMapper().writeValue(response.getOutputStream(), errorResponse);
|
||||
} catch (IOException ex) {
|
||||
log.error("Failed to write error response", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getJwtFromRequest(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(bearerToken) || !bearerToken.startsWith("Bearer ")) {
|
||||
return null;
|
||||
}
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
}
|
||||
@ -18,27 +18,34 @@ import org.springframework.stereotype.Component;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public CustomAuthenticationEntryPoint(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException {
|
||||
if (!isErrorDispatch(request)) {
|
||||
log.error("Custom authentication entry point error", authException);
|
||||
}
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
Response<?> result;
|
||||
if (authException instanceof BadCredentialsException) {
|
||||
result = Response.error(ResponseCode.UNAUTHORIZED, MessageUtils.getMessage(ResponseCode.UNAUTHORIZED.getMessageKey()));
|
||||
} else if (authException instanceof InternalAuthenticationServiceException) {
|
||||
result = Response.error(ResponseCode.USER_NOT_FOUND, MessageUtils.getMessage(ResponseCode.USER_NOT_FOUND.getMessageKey()));
|
||||
} else if (authException instanceof InsufficientAuthenticationException) {
|
||||
result = Response.error(ResponseCode.AUTH_REQUIRED, MessageUtils.getMessage(ResponseCode.AUTH_REQUIRED.getMessageKey()));
|
||||
} else {
|
||||
result = Response.error(ResponseCode.UNAUTHORIZED, MessageUtils.getMessage(ResponseCode.UNAUTHORIZED.getMessageKey()));
|
||||
}
|
||||
log.error("Custom authentication entry point error", authException);
|
||||
response.getWriter().write(objectMapper.writeValueAsString(result));
|
||||
|
||||
Response<?> errorResponse = Response.error(ResponseCode.UNAUTHORIZED);
|
||||
objectMapper.writeValue(response.getOutputStream(), errorResponse);
|
||||
}
|
||||
|
||||
private boolean isErrorDispatch(HttpServletRequest request) {
|
||||
String errorPath = "/error";
|
||||
String requestUri = request.getRequestURI();
|
||||
return requestUri != null && requestUri.equals(errorPath);
|
||||
}
|
||||
}
|
||||
@ -33,3 +33,8 @@ dependency.injection.service.not.found=找不到实体 {0} 对应的服务 (尝
|
||||
dependency.injection.repository.not.found=找不到实体 {0} 对应的Repository: {1}
|
||||
dependency.injection.converter.not.found=找不到实体 {0} 对应的Converter: {1}
|
||||
dependency.injection.entitypath.failed=初始化实体 {0} 的EntityPath失败: {1}
|
||||
|
||||
# JWT相关
|
||||
jwt.token.expired=登录已过期,请重新登录
|
||||
jwt.token.invalid=无效的登录凭证
|
||||
jwt.token.missing=未提供登录凭证
|
||||
@ -33,3 +33,8 @@ dependency.injection.service.not.found=No service found for entity {0} (tried be
|
||||
dependency.injection.repository.not.found=No repository found for entity {0}: {1}
|
||||
dependency.injection.converter.not.found=No converter found for entity {0}: {1}
|
||||
dependency.injection.entitypath.failed=Failed to initialize EntityPath for entity {0}: {1}
|
||||
|
||||
# JWT
|
||||
jwt.token.expired=Login expired, please login again
|
||||
jwt.token.invalid=Invalid token
|
||||
jwt.token.missing=No token provided
|
||||
Loading…
Reference in New Issue
Block a user