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

master
gitee 4 days ago
parent 18cd02517f
commit 0f946a3592

@ -20,7 +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;
@ -29,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","/ollama/generate").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)
@ -52,9 +53,17 @@ public class SecurityConfig {
return http.build();
}
@Bean
public RequestMatcher[] permitAllRequestMatchers() {
return new RequestMatcher[]{
new AntPathRequestMatcher("/auth/login"),
new AntPathRequestMatcher("/ollama/generate")
};
}
@Bean
public AuthenticationProvider authenticationProvider() {
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
// 使用DaoAuthenticationProvider并注入自定义的UserDetailsService和PasswordEncoder
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService); // 从数据库读取用户进行认证

@ -1,19 +1,23 @@
package com.supervision.livedigitalavatarmanage.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.supervision.livedigitalavatarmanage.dto.R;
import com.supervision.livedigitalavatarmanage.dto.UserDetail;
import com.supervision.livedigitalavatarmanage.util.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@Component
@ -21,42 +25,87 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
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 {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// 1. 检查是否在白名单中
if (isPermitAllRequest(request)) {
filterChain.doFilter(request, response);
return;
}
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
// 提取JWT Token去掉前缀"Bearer "
String token = authHeader.substring(7);
//2根本没有 Authorization 头
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
writeTokenErrorResponse(response, "用户未登录,请登录");
return; // 直接返回,不放行
}
String token = authHeader.substring(7); // 去掉 "Bearer "
String username;
// 3解析 Token 失败(格式错误、签名无效、过期等)
try {
// 从JWT中解析用户名
username = jwtUtils.getUsernameFromToken(token);
} catch (Exception e) {
// 如果JWT格式不正确或过期直接放行后续的过滤器会处理认证失败
filterChain.doFilter(request, response);
writeTokenErrorResponse(response, "Token 无效或已过期,请重新登录");
return;
}
// 如果成功提取到用户名,并且当前没有已认证的用户
// 4解析出用户名但 SecurityContext 尚未认证
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 根据用户名从数据库加载用户信息
try {
UserDetail userDetails = (UserDetail) userDetailsService.loadUserByUsername(username);
// 验证登录时间是否一致,如果不一致说明登录已过期
// 验证Token的有效性是否未过期
// 验证 Token 是否仍然有效(时间戳、登录时间等
if (jwtUtils.validateLoginTime(token, userDetails.getLastLoginDate())
&& !jwtUtils.isTokenExpired(token)) {
// 将用户信息封装到Authentication对象中标记为已认证
//认证成功:设置 SecurityContext
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 将Authentication对象放入SecurityContext表示当前请求已通过认证
SecurityContextHolder.getContext().setAuthentication(authToken);
//成功后放行,进入 Controller
filterChain.doFilter(request, response);
return;
} else {
//Token 过期 或 登录态不一致
writeTokenErrorResponse(response, "登录已过期,请重新登录");
return;
}
} catch (UsernameNotFoundException e) {
writeTokenErrorResponse(response, "用户不存在");
return;
} catch (Exception e) {
writeTokenErrorResponse(response, "用户认证异常:" + e.getMessage());
return;
}
}
writeTokenErrorResponse(response, "认证流程异常,请重新登录");
}
filterChain.doFilter(request, 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;
}
}

Loading…
Cancel
Save