This commit is contained in:
王子琦
2026-01-13 13:55:40 +08:00
parent 6affd0c77e
commit f58e05d962
72 changed files with 3251 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package com.flower.security;
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)
public @interface AdminOnly {
}

View File

@@ -0,0 +1,29 @@
package com.flower.security;
import com.flower.user.User;
public class AuthContext {
private static final ThreadLocal<User> HOLDER = new ThreadLocal<>();
private static final ThreadLocal<String> TOKEN = new ThreadLocal<>();
public static void set(User user) {
HOLDER.set(user);
}
public static User get() {
return HOLDER.get();
}
public static void setToken(String token) {
TOKEN.set(token);
}
public static String getToken() {
return TOKEN.get();
}
public static void clear() {
HOLDER.remove();
TOKEN.remove();
}
}

View File

@@ -0,0 +1,74 @@
package com.flower.security;
import com.flower.common.ApiException;
import com.flower.user.Session;
import com.flower.user.SessionRepository;
import com.flower.user.User;
import com.flower.user.UserRepository;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
@Component
public class AuthInterceptor implements HandlerInterceptor {
private final SessionRepository sessionRepository;
private final UserRepository userRepository;
public AuthInterceptor(SessionRepository sessionRepository, UserRepository userRepository) {
this.sessionRepository = sessionRepository;
this.userRepository = userRepository;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
HandlerMethod method = (HandlerMethod) handler;
if (method.hasMethodAnnotation(PublicEndpoint.class)) {
return true;
}
String token = extractToken(request);
if (!StringUtils.hasText(token)) {
throw new ApiException(401, "未登录");
}
Optional<Session> session = sessionRepository.findByTokenAndExpiredFalse(token);
if (!session.isPresent()) {
throw new ApiException(401, "登录已过期");
}
User user = userRepository.findById(session.get().getUserId())
.orElseThrow(() -> new ApiException(404, "用户不存在"));
if (Boolean.TRUE.equals(user.getDisabled())) {
throw new ApiException(403, "账号已禁用");
}
AuthContext.set(user);
AuthContext.setToken(token);
if (method.hasMethodAnnotation(AdminOnly.class) || method.getBeanType().isAnnotationPresent(AdminOnly.class)) {
if (!"ADMIN".equals(user.getRole())) {
throw new ApiException(403, "无权限");
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
AuthContext.clear();
}
private String extractToken(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
if (StringUtils.hasText(auth) && auth.startsWith("Bearer ")) {
return auth.substring(7);
}
return request.getHeader("X-Token");
}
}

View File

@@ -0,0 +1,11 @@
package com.flower.security;
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)
public @interface PublicEndpoint {
}