Я использую Spring boot и пытаюсь создать страницу, которая принимает JWT и аутентифицирует пользователя. Я успешно сделал это с помощью пользовательского OncePerRequestFilter. это решение требует, чтобы пользователь каждый раз предоставлял jwt. но теперь я хочу, чтобы пользователь аутентифицировался один раз, используя jwt, и мог получить доступ к защищенным ресурсам, не предоставляя JWT до конца сеанса. что мне теперь делать?
со следующим кодом, так как для режима sessionCreation задано значение STATELESS в WebSecurityConfigurerAdapter, мы получим http 403 (несанкционированный доступ), если мы не предоставим jwt. с другой стороны, удалив эту строку:
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Я отправляю запрос на поставку jwt один раз. после этого я отправляю запрос без jwt, и в этот раз мне не дают запретить доступ. Я получаю http 500 Internal Server Error.
Трассировка стека:
java.lang.NullPointerException: null
at jwt.JwtTokenVerifier.doFilterInternal(JwtTokenVerifier.java:43) ~[classes/:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_231]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_231]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_231]
OncePerRequestFilter
public class JwtTokenVerifier extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = "";
try {
token = request.getHeader("Authorization");
} catch (Exception e) {
}
if (token != "")
if (token.isEmpty() || token == null || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
try {
token = token.replace("Bearer ", "");
Algorithm alg = Algorithm.HMAC512("fStrongPassWordfStrongPassWordfStrongPassWord");
JWTVerifier verifier = JWT.require(alg).withIssuer("auth0").build(); // Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
String username = jwt.getSubject();
Claim c = jwt.getClaim("auths");
String[] auths = c.asArray(String.class);
Set<GrantedAuthority> ga = new HashSet<>();
for (int i = 0; i < auths.length; i++) {
ga.add(new SimpleGrantedAuthority(auths[i]));
}
Authentication authn = new UsernamePasswordAuthenticationToken(username, null, ga);
// isAuthenticated(
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(authn);
// SecurityContextHolder.getContext().setAuthentication(authn);
HttpSession session = request.getSession(true);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
} catch (JWTVerificationException exception) {
throw new ServletException(String.format("invalid token supplied:\n%s", token));
}
filterChain.doFilter(request, response);
}
}
фильтр для аутентификации пользователя с использованием jwt:
public class jwtUsrPassAuthFilter extends UsernamePasswordAuthenticationFilter{
AuthenticationManager authenticationManager;
public jwtUsrPassAuthFilter(AuthenticationManager authenticationManager) {
super();
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// TODO Auto-generated method stub
try {
UserPassPostObj userpass = new ObjectMapper().readValue(request.getInputStream(), UserPassPostObj.class);
Authentication auth = new UsernamePasswordAuthenticationToken(userpass.getUsername(), userpass.getPassword());
return authenticationManager.authenticate(auth);
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.attemptAuthentication(request, response);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
System.out.println("appending the token to response\n\n\n");
String[] grant_auth = new String[authResult.getAuthorities().size()];
Object[] grants=authResult.getAuthorities().toArray();
for(int i=0;i<authResult.getAuthorities().size();i++) {
grant_auth[i]= grants[i].toString();
}
Algorithm alg = Algorithm.HMAC512("fStrongPassWordfStrongPassWordfStrongPassWord");
String token =JWT.create()
.withIssuer("auth0")
.withSubject(authResult.getName())
.withArrayClaim("auths", grant_auth)
.withIssuedAt(new Date())
.withExpiresAt(java.sql.Date.valueOf(LocalDate.now().plusDays(30)))
.sign(alg)
;
System.out.println(token);
response.addHeader("Authorization", "Bearer "+ token );
}
}
мы добавляем фильтры в websecurity:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
PasswordEncoder passEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new jwtUsrPassAuthFilter(authenticationManager()))
.addFilterAfter(new JwtTokenVerifier(), jwtUsrPassAuthFilter.class)
.authorizeRequests()
.antMatchers("/", "/login**", "/webjars/**","/css/**","/js/**","/tst1").permitAll()
.anyRequest()
.authenticated()
;
}