spring - Spring Boot 和 Spring Security 过滤器未过滤正确的请求
问题描述
我有一个弹簧靴和弹簧安全服务。我扩展了 WebSecurityConfigurerAdapter 类并重写了配置方法。但不知何故,它没有过滤正确的请求。
我的网址类似于 -
localhost:8080/album/private/v1/getAlbumsByVendorId?vendorId=1
localhost:8080/vendor/private/v1/getVendor?vendorId=1
而且我有一些我不想验证的 URL。就像下面的 url。
localhost:8080/category/v1/getCategory
如果 URL 包含私有,则只想进行身份验证。但似乎我的过滤器正在为所有请求调用。有什么问题吗.antMatchers("/**/private/**")
注意 - 我现在没有任何上下文路径。添加了类。控制器只是一个虚拟测试控制器。
@Configuration
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers("/**/private/**").authenticated()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
}
}
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
//Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
@RestController
@CrossOrigin()
public class HelloWorldController {
@RequestMapping({ "/hello" })
public String hello() {
return "Hello World";
}
@RequestMapping({ "/private/test" })
public String hello2() {
return "Hello World-test";
}
@RequestMapping({ "/v1/private/test" })
public String hello3() {
return "Hello World-test-v1";
}
@RequestMapping({ "/v1/public/test" })
public String hello4() {
return "Hello World-test-v1-public";
}
}
解决方案
默认情况下,当 Spring Security 在类路径上时,Spring Boot 将保护所有端点。我们需要明确地为所有其他端点添加一个排除项,以便在没有身份验证的情况下被允许。考虑更改 is .anyRequest().permitAll()
,这意味着每个/**/private/**
人都可以访问除此之外的每个请求。换句话说,过滤器将仅适用于/**/private/**
方法1(干净的方式)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers("/**/private/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
}
方法 2:仅在 Request 来自/private/ 时检查令牌(不是理想的方式)
JwtAuthenticationEntryPoint.java
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
System.out.println("Entry Request: "+request.getRequestURI());
System.out.println("Entry Contain: "+request.getRequestURI().contains("private"));
if(request.getRequestURI().contains("private")==true)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
JwtRequestFilter.java
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
System.out.println("JWT Request: "+request.getRequestURI());
System.out.println("JWT Contain: "+request.getRequestURI().contains("private"));
String username = null;
String jwtToken = null;
//Remove comment for second approach
if(request.getRequestURI().contains("private")==false)
{
System.out.println("Do Noting, Permit It");
chain.doFilter(request, response);
}
else if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ") ) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
http://localhost:8080/v1/private/test **401**
http://localhost:8080/v1/public/test **200**
推荐阅读
- java - 无法在 Windows Server 2012R 64 位上创建新的本机线程
- python-3.x - 从 apscheduler 中的另一个线程删除/停止正在运行的作业
- excel - Excel从连续最后一个填充单元格中的字母数字中提取文本
- typescript - 用抽象方法实例化类
- sympy - 如果集成,SymPy 函数 integration_steps 是否会显示结果?
- php - 用于加密和解密的 PHP 代码在每次执行时创建不同的密文。任何人都可以用 PL/SQL 中的等效代码帮助我吗
- dart - 在 Flutter App 上按下按钮后,如何移动到屏幕下方的特定 Widget?
- kdb - 以字符串为参数的函数
- r - 当每个计算值都依赖于前一个值时如何使用 apply
- xml - 祖先和后代的长形式 - Xpath 轴