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

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

Loading…
Cancel
Save