登录成功
This commit is contained in:
@@ -18,4 +18,35 @@
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Web MVC -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Context -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Common Core dependency -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Common Util dependency -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-util</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Common Redis dependency -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,7 +0,0 @@
|
||||
package org.xyzh;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.xyzh.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @description HttpLogin.java文件描述 HTTP登录注解
|
||||
* @filename HttpLogin.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface HttpLogin {
|
||||
|
||||
/**
|
||||
* @description 是否必需,默认为true
|
||||
* @return boolean
|
||||
* @author yslg
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
/**
|
||||
* @description 当token无效时的错误消息
|
||||
* @return String
|
||||
* @author yslg
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
String message() default "用户未登录或登录已过期";
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.xyzh.common.annotation.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.xyzh.common.annotation.resolver.HttpLoginArgumentResolver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description WebMvcConfig.java文件描述 Web MVC配置
|
||||
* @filename WebMvcConfig.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
private final HttpLoginArgumentResolver httpLoginArgumentResolver;
|
||||
|
||||
/**
|
||||
* 使用构造器注入
|
||||
* 通过接口抽象解决了循环依赖问题,不再需要@Lazy注解
|
||||
*/
|
||||
public WebMvcConfig(HttpLoginArgumentResolver httpLoginArgumentResolver) {
|
||||
this.httpLoginArgumentResolver = httpLoginArgumentResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.add(httpLoginArgumentResolver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.xyzh.common.annotation.resolver;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.xyzh.common.annotation.HttpLogin;
|
||||
import org.xyzh.common.core.domain.LoginDomain;
|
||||
import org.xyzh.common.core.security.TokenParser;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
import org.xyzh.common.utils.NonUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @description HttpLoginArgumentResolver.java文件描述 HTTP登录参数解析器
|
||||
* @filename HttpLoginArgumentResolver.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
@Component
|
||||
public class HttpLoginArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final TokenParser tokenParser;
|
||||
private final RedisService redisService;
|
||||
|
||||
/**
|
||||
* 使用构造器注入,依赖TokenParser接口而非具体实现
|
||||
* 这样避免了与auth模块的直接依赖,解决循环依赖问题
|
||||
*/
|
||||
public HttpLoginArgumentResolver(TokenParser tokenParser,
|
||||
RedisService redisService) {
|
||||
this.tokenParser = tokenParser;
|
||||
this.redisService = redisService;
|
||||
}
|
||||
|
||||
private static final String TOKEN_PREFIX = "Bearer ";
|
||||
private static final String REDIS_LOGIN_PREFIX = "login:token:";
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(HttpLogin.class)
|
||||
&& LoginDomain.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
HttpLogin httpLogin = parameter.getParameterAnnotation(HttpLogin.class);
|
||||
if (httpLogin == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 从请求头中获取token
|
||||
String token = extractTokenFromRequest(webRequest);
|
||||
|
||||
if (NonUtils.isEmpty(token)) {
|
||||
if (httpLogin.required()) {
|
||||
throw new IllegalArgumentException(httpLogin.message());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证token格式和有效性
|
||||
if (!tokenParser.validateToken(token, tokenParser.getUserIdFromToken(token))) {
|
||||
if (httpLogin.required()) {
|
||||
throw new IllegalArgumentException(httpLogin.message());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 从Redis中获取LoginDomain
|
||||
String userId = tokenParser.getUserIdFromToken(token);
|
||||
String redisKey = REDIS_LOGIN_PREFIX + userId;
|
||||
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
|
||||
|
||||
if (loginDomain == null) {
|
||||
if (httpLogin.required()) {
|
||||
throw new IllegalArgumentException(httpLogin.message());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新token信息
|
||||
loginDomain.setToken(token);
|
||||
return loginDomain;
|
||||
|
||||
} catch (Exception e) {
|
||||
if (httpLogin.required()) {
|
||||
throw new IllegalArgumentException(httpLogin.message());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 从请求中提取token
|
||||
* @param webRequest 请求对象
|
||||
* @return String token
|
||||
* @author yslg
|
||||
* @since 2025-10-06
|
||||
*/
|
||||
private String extractTokenFromRequest(NativeWebRequest webRequest) {
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 优先从Authorization头获取
|
||||
String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if (NonUtils.isNotEmpty(authHeader) && authHeader.startsWith(TOKEN_PREFIX)) {
|
||||
return authHeader.substring(TOKEN_PREFIX.length());
|
||||
}
|
||||
|
||||
// 从请求参数中获取token
|
||||
String token = request.getParameter("token");
|
||||
if (NonUtils.isNotEmpty(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
// 从请求头中获取token
|
||||
token = request.getHeader("token");
|
||||
if (NonUtils.isNotEmpty(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user