[英]Unable to skip the OncePerRequestFilter filter for a login url (basically to get the JWT token during the initial login)in spring boot security
I am trying a develop a spring boot rest API with JWT authorization using spring security. I am trying a develop a spring boot rest API with JWT authorization using spring security. I want all of my request to go through the filter to validate the JWT token except for the
/authenticate
request which should generate the jwt token.我希望我对 go 的所有请求都通过过滤器来验证 JWT 令牌,但应该生成 jwt 令牌的
/authenticate
请求除外。 But with the below code, the /authenticate
request is also getting intercepted by the filter due to which its failing with 401. Please let me know what am I missing in the below code.但是使用下面的代码,
/authenticate
请求也被过滤器拦截,因为它以 401 失败。请让我知道我在下面的代码中遗漏了什么。
JwtTokenFilter class JwtTokenFilter class
@Component
public class JwtTokenFilter extends OncePerRequestFilter
{
@Autowired
private UserService 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);
}
}
JwtConfig class JwtConfig class
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class JwtConfigurer extends WebSecurityConfigurerAdapter
{
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserService jwtUserDetailsService;
@Autowired
private JwtTokenFilter 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
{
// We don't need CSRF for this example
httpSecurity.csrf().disable().
// dont authenticate this particular request
authorizeRequests().antMatchers("/authenticate").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterAfter(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Controller class Controller class
@RestController
@CrossOrigin
public class JwtAuthenticationController
{
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserService userDetailsService;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody User authenticationRequest) throws Exception
{
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
User u = new User();
u.setUsername(authenticationRequest.getUsername());
u.setToken(token);
return ResponseEntity.ok(u);
}
private void authenticate(String username, String password) throws Exception
{
try
{
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
catch (DisabledException e)
{
throw new Exception("USER_DISABLED", e);
}
catch (BadCredentialsException e)
{
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
I struggled with this for two days and the best solution was the Tom answer combined with this setup on my SecurityConfig
:我为此苦苦挣扎了两天,最好的解决方案是Tom 的答案与我的
SecurityConfig
上的此设置相结合:
override fun configure(http: HttpSecurity?) {
// Disable CORS
http!!.cors().disable()
// Disable CSRF
http.csrf().disable()
// Set session management to stateless
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//Add JwtTokenFilter
http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter::class.java)
}
Basically, OncePerRequestFilter works in that way only.基本上,OncePerRequestFilter 仅以这种方式工作。 Not sure if this can be avoided.
不确定这是否可以避免。 Quoting the documentation:
引用文档:
Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container.
过滤器基础 class 旨在保证在任何 servlet 容器上每次请求分派一次执行。
You can try adding the method type as well to skip teh authentication on the endpoint.您也可以尝试添加方法类型以跳过端点上的身份验证。
.antMatchers(HttpMethod.GET, "/authenticate").permitAll()
As already pointed by Mohit, even i couldn't see any mistakes in your configuration.正如 Mohit 已经指出的那样,即使我在您的配置中也看不到任何错误。
If you understand below explanation, it will help you to resolve.如果您理解下面的解释,它将帮助您解决。
Even though /authenticate
request is permitAll configured the request should pass through your JWT Filter.即使
/authenticate
请求是 permitAll 配置,该请求也应通过您的 JWT 过滤器。 But FilterSecurityInterceptor
is the last filter it will check for configured antMatchers and associated restrictions/permissions based on that it will decide whether request should be permitted or denied.但是
FilterSecurityInterceptor
是最后一个过滤器,它将检查配置的 antMatchers 和相关的限制/权限,基于它将决定是允许还是拒绝请求。
For /authenticate
method it should pass through filter and requestTokenHeader, username should be null and make sure chain.doFilter(request, response);
对于
/authenticate
方法,它应该通过过滤器和 requestTokenHeader,用户名应该是 null 并确保chain.doFilter(request, response);
is reaching without any exceptions.无一例外地到达。
And when it reaches FilterSecurityInterceptor
and If you have set log level to debug) logs similar as given below should be printed.并且当它到达
FilterSecurityInterceptor
并且如果您已将日志级别设置为调试)应打印类似于下面给出的日志。
DEBUG - /app/admin/app-config at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG - Checking match of request : '/app/admin/app-config'; against '/resources/**'
DEBUG - Checking match of request : '/app/admin/app-config'; against '/'
DEBUG - Checking match of request : '/app/admin/app-config'; against '/login'
DEBUG - Checking match of request : '/app/admin/app-config'; against '/api/**'
DEBUG - Checking match of request : '/app/admin/app-config'; against '/app/admin/app-config'
DEBUG - Secure object: FilterInvocation: URL: /app/admin/app-config; Attributes: [permitAll]
DEBUG - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@511cd205: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 696171A944493ACA1A0F7D560D93D42B; Granted Authorities: ROLE_ANONYMOUS
DEBUG - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@6df827bf, returned: 1
DEBUG - Authorization successful
Attach those logs, so that then problem can be predicted.附上这些日志,以便可以预测问题。
Write a configuration class that implements org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter and override the configur method like so:编写实现org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter的配置 class 并像这样覆盖 configur 方法:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// dont authenticate this particular request. you can use a wild card here. e.g /unprotected/**
httpSecurity.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll().
//authenticate everything else
anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
I had similar problem, and I overcome it by comparing request path to the path I do not want to filter.我有类似的问题,我通过将请求路径与我不想过滤的路径进行比较来克服它。
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//To skip OncePerRequestFilter for authenticate endpoint
if(request.getServletPath().equals("/authenticate")){
filterChain.doFilter(request, response);
return;
}
// filter logic continue..
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.