spring: аутентифицировать пользователя с помощью JWT один раз и разрешить доступ до конца сеанса - PullRequest
0 голосов
/ 09 февраля 2020

Я использую 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()
        ;

}

1 Ответ

0 голосов
/ 12 февраля 2020

Я отвечаю на свой вопрос. удаления упомянутой мною строки было достаточно, чтобы заставить spring поддерживать аутентификацию пользователя в течение оставшейся части сеанса. очевидно, по умолчанию SessionCreationPolicy имеет значение IF_REQUIRED. это то же самое, что и использование следующего кода внутри WebSecurityConfigurerAdapter:

.sessionManagement (). sessionCreationPolicy (SessionCreationPolicy.IF_REQUIRED)

проблема заключалась в исключении нулевого указателя, которое я решается так:

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");
            token = request.getParameter("authorization");
            if (token == "" || token==null) {
                filterChain.doFilter(request, response);
                return;
            }
        } catch (Exception e) {
        }

        if (token.isEmpty() || token == null || !token.startsWith("Bearer")) {

            filterChain.doFilter(request, response);
            return;
        }

        try {
            token = token.replace("Bearer", "");
            Algorithm alg = Algorithm.HMAC512("fuckingStrongPassWordfuckingStrongPassWordfuckingStrongPassWord");
            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);

    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...