РЕДАКТИРОВАТЬ: теперь я получаю 403 запрещенных, доступ запрещен для всех запросов, кроме регистрации и входа в систему
Я проверяю свои конечные точки для приложения после реализации аутентификации JWT и весны безопасность, основанная на этой статье: https://medium.com/@hantsy / protect-rest-apis-with-spring-security-and-jwt-5fbc90305cc5
Аналогично, мои другие источники вдохновения включают в себя: https://www.youtube.com/watch?v=X80nJ5T7YpE
Регистрация и вход в систему пользователя работоспособны, но когда я выполняю последующие запросы, требующие JWT в заголовке, я получаю бесконечный цикл / рекурсию и ошибку переполнения стека. Однако, когда я специально ввожу несуществующий JWT, он возвращает соответствующее сообщение об ошибке и работает хорошо. Таким образом, проблема заключается в успешной аутентификации действительного JWT. После просмотра в Интернете различных ответов о переполнении стека, таких как: Бесконечный l oop в пользовательском приложении Spring Security Бесконечный l oop в пользовательском приложении Spring Security В Spring Security 3.2 .5, что вызывает бесконечное l oop внутри реализации AuthenticationManager?
Кажется, что все они говорят одно и то же: класс ProviderManager просматривает список поставщиков аутентификации, чтобы найти тот, который может аутентифицировать данную аутентификацию, и он не находит тот, который вызывает эту ошибку, и они предлагают реализовать ваш собственный поставщик аутентификации. Ниже приведен мой код для различных соответствующих классов. Обратите внимание, что я не использую класс userDetailsService; вместо этого у меня есть свой собственный класс UserService, который традиционно имеет функциональность userDetailsService, а также код приложения c. Я также не реализовал свой собственный поставщик аутентификации, следуя приведенным выше примерам кода, поэтому я хотел бы избежать реализации поставщиков аутентификации, поскольку упомянутые примеры кода работают и являются более простыми.
Класс JwtProvider
@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;
}
}
}
Класс JwtConfigurer
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);
}
}
Класс JwtFilter
// 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;
}
}
Класс SecurityConfigurer
@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, поэтому любая помощь приветствуется спасибо