1.修改权限认证逻辑,如果权限认证不通过直接在过滤器中返回

2. 修改nginx配置文件
main
gitee 2 days ago
parent b3a560a6ca
commit 15e8c2f4dc

@ -26,4 +26,15 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ai-platform/ws/ {
proxy_pass http://ai-platform-server/ai-platform/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}

@ -41,4 +41,14 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ai-platform/ws/ {
proxy_pass http://ai-platform-server/ai-platform/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}

@ -20,6 +20,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.util.Base64;
@ -28,22 +30,22 @@ import java.util.Base64;
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain securityFilterChain(HttpSecurity http,
RequestMatcher[] permitAllRequestMatcher,
JwtAuthenticationFilter jwtAuthenticationFilter,
AuthenticationProvider authenticationProvider) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // 禁用CSRF
.cors(Customizer.withDefaults())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login","/agent/streamChat","/livetalking/chatCallBack").permitAll()
.requestMatchers(permitAllRequestMatcher).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider())
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
// 禁用 formLogin 和 httpBasic
.formLogin(AbstractHttpConfigurer::disable)
@ -51,9 +53,17 @@ public class SecurityConfig {
return http.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
public RequestMatcher[] permitAllRequestMatchers() {
return new RequestMatcher[] {
new AntPathRequestMatcher("/auth/login"),
new AntPathRequestMatcher("/agent/streamChat"),
new AntPathRequestMatcher("/livetalking/chatCallBack"),
new AntPathRequestMatcher("/ws/**")
};
}
@Bean
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
// 使用DaoAuthenticationProvider并注入自定义的UserDetailsService和PasswordEncoder
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService); // 从数据库读取用户进行认证

@ -1,5 +1,7 @@
package com.supervision.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.supervision.dto.R;
import com.supervision.util.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@ -7,14 +9,17 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@ -23,40 +28,88 @@ import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
private final UserDetailsService userDetailsService;
private final ObjectMapper objectMapper;
private final RequestMatcher[] permitAllRequestMatchers;
@Override
protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response,
@NonNull FilterChain filterChain) throws ServletException, IOException {
// 1. 检查是否在白名单中
if (isPermitAllRequest(request)) {
filterChain.doFilter(request, response);
return;
}
// 获取 Authorization 头
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
// 提取JWT Token去掉前缀"Bearer "
String token = authHeader.substring(7);
String username;
// 提取 token去掉 "Bearer " 前缀)
String token = authHeader.substring(7);
String username;
// 2解析 token 失败(格式错误、签名无效、过期等)
try {
username = jwtUtils.getUsernameFromToken(token);
} catch (Exception e) {
writeTokenErrorResponse(response, "Token 无效或已过期,请重新登录");
return; // 中断
}
// 3成功解析出用户名且当前 SecurityContext 未认证
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
try {
// 从JWT中解析用户名
username = jwtUtils.getUsernameFromToken(token);
} catch (Exception e) {
// 如果JWT格式不正确或过期直接放行后续的过滤器会处理认证失败
filterChain.doFilter(request, response);
return;
}
// 如果成功提取到用户名,并且当前没有已认证的用户
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 根据用户名从数据库加载用户信息
// 从 UserDetailsService 加载用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 验证Token的有效性是否未过期
// 验证 Token 是否未过期(你可以根据需要添加更多验证,如登录时间)
if (!jwtUtils.isTokenExpired(token)) {
// 将用户信息封装到Authentication对象中标记为已认证
// 创建认证对象
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 将Authentication对象放入SecurityContext表示当前请求已通过认证
// 设置到 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authToken);
// 认证成功!放行到 Controller
filterChain.doFilter(request, response);
return;
} else {
// Token 已过期
writeTokenErrorResponse(response, "Token 已过期,请重新登录");
return;
}
} catch (UsernameNotFoundException e) {
// 用户不存在
writeTokenErrorResponse(response, "用户不存在或已被删除");
return;
} catch (Exception e) {
// 其他加载异常
writeTokenErrorResponse(response, "用户信息加载失败:" + e.getMessage());
return;
}
}
filterChain.doFilter(request, response);
// 特殊情况兜底:比如 token 解析成功但 userDetails 为 null或已认证但不符合预期
// 根据你的“核心逻辑”,只要没成功放行,就视为失败
writeTokenErrorResponse(response, "认证失败,请重新登录");
}
private void writeTokenErrorResponse(HttpServletResponse response, String message) throws IOException {
response.setStatus(HttpStatus.OK.value()); // 设置为 200
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
objectMapper.writeValue(response.getWriter(), R.fail(401, message));
}
private boolean isPermitAllRequest(HttpServletRequest request) {
for (RequestMatcher matcher : permitAllRequestMatchers) {
if (matcher.matches(request)) {
return true;
}
}
return false;
}
}

@ -31,27 +31,32 @@ public class DanmakuWebSocketHandler extends TextWebSocketHandler {
private final LivetalkingService livetalkingService;
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) {
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws IOException {
// 解析客户端消息
DanmakuMessage danmaku = DanmakuMessage.fromJson(textMessage.getPayload());
String roomId = getRoomIdFromSession(session);
danmaku.setRoomId(roomId);
try {
DanmakuMessage danmaku = DanmakuMessage.fromJson(textMessage.getPayload());
String roomId = getRoomIdFromSession(session);
danmaku.setRoomId(roomId);
// 验证消息
validateDanmaku(danmaku);
// 验证消息
validateDanmaku(danmaku);
// 保存到日志
String messageId = dialogueLogService.saveLog(danmaku);
// 保存到日志
String messageId = dialogueLogService.saveLog(danmaku);
// 发布到消息中心
DanmakuPublisher.getInstance().publish(danmaku);
// 发布到消息中心
DanmakuPublisher.getInstance().publish(danmaku);
// 判断是否需要回复
if (isQuestion(danmaku.getContent())) {
log.info("检测到问题: {}", danmaku.getContent());
LivetalkingChatDTO livetalkingChatDTO = new LivetalkingChatDTO(danmaku);
livetalkingChatDTO.setMessageId(messageId);
livetalkingService.chat(livetalkingChatDTO);
// 判断是否需要回复
if (isQuestion(danmaku.getContent())) {
log.info("检测到问题: {}", danmaku.getContent());
LivetalkingChatDTO livetalkingChatDTO = new LivetalkingChatDTO(danmaku);
livetalkingChatDTO.setMessageId(messageId);
livetalkingService.chat(livetalkingChatDTO);
}
} catch (Exception e) {
log.info("处理弹幕消息失败: {}", e.getMessage());
afterConnectionClosed(session, CloseStatus.BAD_DATA);
}
}

Loading…
Cancel
Save