no message

This commit is contained in:
wangjianhong
2025-08-06 15:54:19 +08:00
parent bca87e7b0d
commit 417856b7be
6 changed files with 108 additions and 61 deletions

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
## Getting started
##
```
mvn versions:display-dependency-updates
mvn versions:update-properties
mvn versions:set -DnewVersion=1.2.0.RELEASE
```

10
pom.xml
View File

@@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<version>4.0.0-M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.arrokoth</groupId>
<artifactId>authorization-server-standalone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>1.2.0.RELEASE</version>
<name>authorization-server-standalone</name>
<description>authorization-server-standalone</description>
<url/>
@@ -34,8 +34,8 @@
<maven.compiler.target>17</maven.compiler.target>
<spring.boot.version>3.5.3</spring.boot.version>
<arrokoth.version>1.1.0.RELEASE</arrokoth.version>
<arrokoth.bom.version>1.1.0.RELEASE</arrokoth.bom.version>
<arrokoth.version>1.2.0.RELEASE</arrokoth.version>
<arrokoth.bom.version>1.2.0.RELEASE</arrokoth.bom.version>
</properties>
<dependencies>
@@ -50,7 +50,7 @@
<dependency>
<groupId>com.arrokoth.framework</groupId>
<artifactId>basic-authorization-server</artifactId>
<version>1.0.1-RELEASE</version>
<version>1.2.0.RELEASE</version>
</dependency>
<dependency>

View File

@@ -50,7 +50,7 @@ public class SecurityWebAutoConfigurer {
.logout(logout -> logout
.logoutUrl(SecurityWebProperties.AXIOS_LOGOUT_PROCESSING_URL)
.logoutSuccessUrl(securityWebProperties.getLogoutSuccessUrl())
// .logoutSuccessUrl(securityWebProperties.getLogoutSuccessUrl())
.invalidateHttpSession(true) // 注销时销毁 session
.deleteCookies("JSESSIONID", "Authorization")
.permitAll()

View File

@@ -12,6 +12,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -28,92 +29,113 @@ public class JwtRequestFilter extends OncePerRequestFilter {
private final RedisTokenService redisTokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
logRequestDetails(request);
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
// 1. 提取 JWT Token
String jwt = extractJwtFromRequest(request);
if (jwt == null) {
chain.doFilter(request, response);
return;
}
String username = extractUsernameFromToken(jwt);
if (username == null) {
chain.doFilter(request, response);
// 2. 解析用户名
String username = JwtUtils.extractUsername(jwt);
if (username == null || username.isBlank()) {
log.warn("JWT token does not contain a valid username: {}", maskToken(jwt));
sendUnauthorizedResponse(response, "Invalid token");
return;
}
if (isTokenBlacklisted(jwt, response)) {
// 3. 检查 Token 是否被拉黑(退出登录状态)
if (redisTokenService.isBlacklisted(jwt)) {
log.warn("Token is blacklisted: {}", maskToken(jwt));
sendUnauthorizedResponse(response, "Token is blacklisted");
return;
}
// 4. 若用户未认证,则进行认证
if (isUserNotAuthenticated(username)) {
authenticateUser(jwt, username);
authenticateUser(jwt, username, request);
}
// 5. 继续过滤链
chain.doFilter(request, response);
} catch (Exception ex) {
logger.warn("Error occurred during JWT filter processing", ex);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
log.warn("JWT filter processing failed for request: {}", request.getRequestURI(), ex);
sendUnauthorizedResponse(response, "Unauthorized: " + ex.getMessage());
}
}
/**
* 从请求中提取 JWT Token
*/
private String extractJwtFromRequest(HttpServletRequest request) {
String jwt = null;
// 1. 先从 Authorization Header 中提取
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
// 优先从 Authorization: Bearer <token>
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7).trim();
}
// 2. 如果 Header 中没有,则尝试从 URL 参数获取(如 X-Token
if (jwt == null || jwt.isEmpty()) {
jwt = request.getParameter("X-Token");
// 其次尝试从 X-Token 参数获取(URL 或表单
String tokenParam = request.getParameter("X-Token");
if (tokenParam != null && !tokenParam.isBlank()) {
return tokenParam.trim();
}
return jwt;
}
private String extractUsernameFromToken(String jwt) {
try {
return JwtUtils.extractUsername(jwt);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
private boolean isTokenBlacklisted(String jwt, HttpServletResponse response) throws IOException {
if (redisTokenService.isBlacklisted(jwt)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token is blacklisted");
return true;
}
return false;
return null;
}
/**
* 判断当前上下文是否已认证为指定用户
*/
private boolean isUserNotAuthenticated(String username) {
return SecurityContextHolder.getContext().getAuthentication() == null ||
!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() ||
!SecurityContextHolder.getContext().getAuthentication().getName().equals(username);
var authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication == null
|| !authentication.isAuthenticated()
|| !username.equals(authentication.getName());
}
private void authenticateUser(String jwt, String username) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
/**
* 对用户进行身份认证并设置 SecurityContext
*/
private void authenticateUser(String jwt, String username, HttpServletRequest request) {
UserDetails userDetails;
try {
userDetails = userDetailsService.loadUserByUsername(username);
} catch (Exception e) {
log.warn("User not found during JWT authentication: {}", username);
throw new RuntimeException("Invalid token or user does not exist");
}
if (JwtUtils.validateToken(jwt, userDetails.getUsername())) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
if (!JwtUtils.validateToken(jwt, userDetails.getUsername())) {
log.warn("JWT token validation failed for user: {}", username);
throw new RuntimeException("Token is expired or invalid");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("Authenticated user: {} via JWT", username);
}
/**
* 发送 401 响应
*/
private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
}
/**
* 日志脱敏:隐藏长 Token 的中间部分
*/
private String maskToken(String token) {
if (token == null || token.length() <= 10) return token;
return token.substring(0, 5) + "..." + token.substring(token.length() - 5);
}
/**
* 封装的打印请求详情方法
@@ -123,10 +145,10 @@ public class JwtRequestFilter extends OncePerRequestFilter {
String queryString = request.getQueryString();
// 打印基本信息
if (log.isInfoEnabled()) {
log.info("Request URL: {}", requestURL);
if (log.isDebugEnabled()) {
log.debug("Request URL: {}", requestURL);
if (queryString != null) {
log.info("Query Parameters: {}", queryString);
log.debug("Query Parameters: {}", queryString);
}
}
@@ -135,9 +157,11 @@ public class JwtRequestFilter extends OncePerRequestFilter {
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (log.isDebugEnabled()) {
for (String value : paramValues) {
log.debug("Request Param: {} = {}", paramName, value);
}
}
}
}
}

View File

@@ -19,7 +19,15 @@ public class UserDetailsServiceStore {
.roles("admin", "normal")
.authorities("app", "web")
.build();
return new InMemoryUserDetailsManager(user);
UserDetails user2 = User.withUsername("guest")
.password(passwordEncoder.encode("yyds@8848"))
.roles("normal")
.authorities("app")
.build();
return new InMemoryUserDetailsManager(user,user2);
}
}

View File

@@ -69,7 +69,7 @@ mybatis:
mapper-locations: classpath*:com.arrokoth/**/mapper/xml/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus:
type-aliases-package: com.arrokoth.**.domain
mapper-locations: classpath*:com.arrokoth/**/mapper/xml/*.xml