验证用户时的无限循环:使用Spring安全性实现JWT
编辑:除注册和登录外,我现在收到403禁止访问、拒绝访问的请求 在基于本文实现了JWT身份验证和spring安全性之后,我正在测试我的应用程序端点: 同样,我的其他灵感来源包括: 用户的注册和登录是功能性的,但是当我执行后续请求时需要在头中使用JWT,我会得到一个无限循环/递归和一个堆栈溢出错误。然而,当我故意输入一个不存在的JWT时,它会抛出相应的错误消息,并且运行良好。因此,问题在于成功验证有效的JWT。在线查看不同的堆栈溢出答案后,如: 它们似乎都说了同样的话,ProviderManager类正在查看身份验证提供程序列表,以找到一个可以对给定身份验证进行身份验证的提供程序,但没有找到,这导致了此错误,它们建议实现您自己的身份验证提供程序。下面是我针对不同相关类的代码。注意,我没有使用userDetailsService类;相反,我有自己的UserService类,它基本上具有userDetailsService的功能,还具有特定于应用程序的代码。按照上面提到的代码示例,我也没有实现我自己的身份验证提供程序,因此我希望避免实现身份验证提供程序,因为上面提到的代码示例可以工作并且更简单 JwtProvider类验证用户时的无限循环:使用Spring安全性实现JWT,spring,spring-boot,jwt,Spring,Spring Boot,Jwt,编辑:除注册和登录外,我现在收到403禁止访问、拒绝访问的请求 在基于本文实现了JWT身份验证和spring安全性之后,我正在测试我的应用程序端点: 同样,我的其他灵感来源包括: 用户的注册和登录是功能性的,但是当我执行后续请求时需要在头中使用JWT,我会得到一个无限循环/递归和一个堆栈溢出错误。然而,当我故意输入一个不存在的JWT时,它会抛出相应的错误消息,并且运行良好。因此,问题在于成功验证有效的JWT。在线查看不同的堆栈溢出答案后,如: 它们似乎都说了同样的话,ProviderManag
@Component
public class JwtProvider {
@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
@Autowired
private AppUserService appUserService;
// encoding the secret used to create the signature for the JWT
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public String createToken(String userEmail) {
Claims claims = Jwts.claims().setSubject(userEmail); // subject is the person who is being authenticated
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secretKey)//
.compact();
}
public Authentication getAuthentication(String token) {
AppUser user = this.appUserService.getAppUserByEmail(getUserEmail(token));
return new UsernamePasswordAuthenticationToken(user, "");
}
// get userEmail in the jwt token that we receive
public String getUserEmail(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
// validate a token that we have: not expired, and matches the userEmail
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
package ca.mcgill.ecse321.petadoption.controller;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
// configures how we filter http requests
// specifies order of applying filters
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private JwtProvider jwtProvider;
public JwtConfigurer(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
JwtFilter customFilter = new JwtFilter(jwtProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
// Job: intercept a request and look at the header to get the JWT
public class JwtFilter extends GenericFilterBean {
private JwtProvider jwtProvider;
public JwtFilter(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
String token = resolveToken((HttpServletRequest) req);
if (token != null && jwtProvider.validateToken(token)) { // if token is not null and it isn't expired and it is valid
Authentication auth = jwtProvider.getAuthentication(token); // returning the UsernamePasswordAuthenticationToken
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, res);
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
安全配置器类
@Configuration
@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
JwtProvider jwtProvider;
@Autowired
AppUserService appUserService;
// this is because we cannot perform @autowired authManager anymore cuz its only compatible w/ older Spring Boot
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable() // this disables cross-site request forgery where an attacker manages to get an authenticated user to perform a malicious request to his liking using diff links and such
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // tell it to use the filterChain that intercepts requests and checks the authorization header for a jwt
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/login/").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/register/").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtProvider)); // this specifies to add our custom filter before the usernamepasswordauthenticationfilter
}
}
以下是我得到的错误:
2020-03-14 12:14:27.183 ERROR 784 --- [nio-8081-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause
java.lang.StackOverflowError: null
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
我对Spring和Spring Boot比较陌生,因此非常感谢您的帮助。谢谢