82.2.1. OncePerRequestFilter
package cn.netkiller.config;
import cn.netkiller.annotation.TokenPass;
import cn.netkiller.component.JwtTokeComponent;
import cn.netkiller.utils.ResponseJson;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Component
@Slf4j
public class SecurityTokenAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private JwtTokeComponent jwtTokeComponent;
@Autowired
// @Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver handlerExceptionResolver;
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String url = request.getRequestURL().toString();
log.info(request.getRequestURL().toString());
// return super.shouldNotFilter(request);
return url.contains("/exclude");
}
/**
* token过滤器配置
*
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
// 没有携带 token 则 放行
filterChain.doFilter(request, response);
return;
}
log.info("doFilterInternal: " + request.getRequestURI());
log.info("doFilterInternal: " + request.getHttpServletMapping().getPattern());
try {
// if (token == null) {
// throw new RuntimeException("无 token");
// }
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
// Map<RequestMappingInfo, HandlerMethod> handlerMethods = req2HandlerMapping.getHandlerMethods();
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
if (Objects.nonNull(handlerExecutionChain)) {
HandlerMethod handlerMethod = (HandlerMethod) handlerExecutionChain.getHandler();
// if (handlerMethod.getBeanType().getName().startsWith(MY_CONTROLLER_PACKAGE_NAME)) {
// log.info(handlerMethod.getBeanType().getSimpleName());
// }
Method method = handlerMethod.getMethod();
//检查方法是否有TokenPass注解,有则跳过认证,直接通过
if (method.isAnnotationPresent(TokenPass.class)) {
TokenPass tokenPass = method.getAnnotation(TokenPass.class);
if (tokenPass.required()) {
filterChain.doFilter(request, response);
return;
}
}
//检查 TokenVerification 需要用户权限的注解
// if (method.isAnnotationPresent(TokenVerification.class)) {
// TokenVerification tokenVerification = method.getAnnotation(TokenVerification.class);
// if (tokenVerification.required()) {
// //
// }
// }
// token 校验逻辑写在这里
// log.info("token: " + token);
ResponseJson jwt = jwtTokeComponent.verifier(token);
// log.info("jwt: " + jwt.isStatus());
if (!jwt.isStatus()) {
throw new RuntimeException(jwt.getReason());
}
// 执行认证
Set<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("ROLE_WATCH"));
authorities.add(new SimpleGrantedAuthority("ROLE_PICTURE"));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("netkiller", null, authorities);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
log.info(authenticationToken.toString());
}
} catch (Exception e) {
log.info(e.getMessage());
handlerExceptionResolver.resolveException(request, response, null, e);
return;
}
/**
* 将请求转发给过滤器链下一个filter
*/
filterChain.doFilter(request, response);
}
}
82.2.2. SecurityConfiguration
package cn.netkiller.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @author Neo
* @description Security 配置类
* @date 2023-01-26 21:18
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
@Autowired
private SecurityTokenAuthenticationFilter securityTokenAuthenticationFilter;
// @Value("${spring.profiles.active}")
// private String env;
@Bean
public WebSecurityCustomizer ignoringCustomizer() {
return (web) -> web.ignoring().requestMatchers("/token", "/version");
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// http.authorizeRequests().anyRequest().permitAll();
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> {
authorize
.requestMatchers("/", "/ping", "/exclude", "/mock/**", "/test/**").permitAll()
.requestMatchers("/token").permitAll()
.requestMatchers("/picture/**", "/chat/**", "/badges/**", "/album/**", "/book/**").permitAll()
.requestMatchers("/static/**", "/resources/**").permitAll()
.anyRequest().authenticated();
}
)
.addFilterBefore(securityTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}