![](/img/trans.png)
[英]JWT token filter not working for spring security for mobile OTP based login
[英]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. 我希望我對 go 的所有請求都通過過濾器來驗證 JWT 令牌,但應該生成 jwt 令牌的/authenticate
請求除外。 但是使用下面的代碼, /authenticate
請求也被過濾器攔截,因為它以 401 失敗。請讓我知道我在下面的代碼中遺漏了什么。
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
@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
@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);
}
}
}
我為此苦苦掙扎了兩天,最好的解決方案是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)
}
基本上,OncePerRequestFilter 僅以這種方式工作。 不確定這是否可以避免。 引用文檔:
過濾器基礎 class 旨在保證在任何 servlet 容器上每次請求分派一次執行。
您也可以嘗試添加方法類型以跳過端點上的身份驗證。 .antMatchers(HttpMethod.GET, "/authenticate").permitAll()
正如 Mohit 已經指出的那樣,即使我在您的配置中也看不到任何錯誤。
如果您理解下面的解釋,它將幫助您解決。
即使/authenticate
請求是 permitAll 配置,該請求也應通過您的 JWT 過濾器。 但是FilterSecurityInterceptor
是最后一個過濾器,它將檢查配置的 antMatchers 和相關的限制/權限,基於它將決定是允許還是拒絕請求。
對於/authenticate
方法,它應該通過過濾器和 requestTokenHeader,用戶名應該是 null 並確保chain.doFilter(request, response);
無一例外地到達。
並且當它到達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
附上這些日志,以便可以預測問題。
編寫實現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);
}
我有類似的問題,我通過將請求路徑與我不想過濾的路徑進行比較來克服它。
@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.