【问题标题】:spring security permitAll() not working for JWT Authentication filterspring security permitAll() 不适用于 JWT 身份验证过滤器
【发布时间】:2021-01-20 00:49:22
【问题描述】:

问题在于应用程序使用自定义 JWT 身份验证过滤器,该过滤器扩展了 UsernamePasswordAuthenticationFilter,它接受用户凭据并生成一个长期存在的 JWT 作为回报。

问题似乎出在permitAll() 上,它应该绕过自定义授权过滤器。但是在调试模式下,我可以看到首先调用自定义JwtAuthorizationFilter 而不是自定义JwtAuthenticationFilter 过滤器,最终导致403 禁止访问被拒绝响应。

注意.antMatchers(HttpMethod.POST, "/login").permitAll() 行。 /login 端点应该可以在没有 JWT 的情况下访问,因为当用户尚未登录时,JWT 尚未生成。

下面是我的代码

JwtAuthenticationFilter.java

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    //
    private AuthenticationManager authenticationManager;

    private final static UrlPathHelper urlPathHelper = new UrlPathHelper();

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        setFilterProcessesUrl("/login");
    }

    /**
     * Trigger when we issue POST request to login / we also need to pass in
     * {"username: " username, "password": password} in the request body
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        // Grab credentials and map them to login viewmodel

        LoginViewModel credentials = null;

        try {
            credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Create login token
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                credentials.getUsername(), credentials.getPassword(), new ArrayList<>());

        // Authenciate user
        Authentication auth = authenticationManager.authenticate(authenticationToken);

        return auth;
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        // Grab principal
        UserPrincipal principal = (UserPrincipal) authResult.getPrincipal();

        // Create JWT Token
        String token = JWT.create().withSubject(principal.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
                .sign(HMAC512(JwtProperties.SECRET.getBytes()));

        // add token in response
        response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + token);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException failed) throws IOException, ServletException {
        logger.debug("failed authentication while attempting to access "
                + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));

        // Add more descriptive message
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
    }

}

JwtAuthorizationFilter.java

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private UserRepository userRepository;

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
        super(authenticationManager);
        this.userRepository = userRepository;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        //Read the Authorization header, where the JWT token should be
        String header = request.getHeader(JwtProperties.HEADER_STRING);

        //If header does not contain BEARER or is null delegate to Spring impl and exit
        if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }


        // If header is present, try grab user principal from db and perform authorization
        Authentication authentication = getUsernamePasswordAuthentication(request);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        // Continue filter execution
        chain.doFilter(request, response);
    }

    private Authentication getUsernamePasswordAuthentication(HttpServletRequest request){
        String token = request.getHeader(JwtProperties.HEADER_STRING)
                .replace(JwtProperties.TOKEN_PREFIX, "");

        if(token !=null){
            //parse the token validate it
            String userName = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()))
                    .build()
                    .verify(token)
                    .getSubject();
            // Search in the DB if we find the user by token subject(username)
            // If so, then grab user details and create auth token using username, pass, authorities/roles

            if(userName != null){
                User user = userRepository.findByUsername(userName);
                UserPrincipal principal = new UserPrincipal(user);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, null, principal.getAuthorities());
                return authenticationToken;
            }
            return null;
        }
        return null;
    }
}

SecurityConfiguration.java

Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private UserPrincipalDetailsService userPrincipalDetailsService;
    private UserRepository userRepository;

    public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService,
            UserRepository userRepository) {
        this.userPrincipalDetailsService = userPrincipalDetailsService;
        this.userRepository = userRepository;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(authenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // remove csrf state in session because in jwt do not need them
                .csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers("/api/public/management/*").hasRole("MANAGER").antMatchers("/api/public/admin/*")
                .hasRole("ADMIN").anyRequest().authenticated().and()
                // add jwt filters (1. authentication, 2. authorization_)
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager(), this.userRepository));
        // configure access rules

    }

    @Bean
    DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService((UserDetailsService) this.userPrincipalDetailsService);

        return daoAuthenticationProvider;
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

请求,响应

有人可以在这里提出什么问题..感谢您的帮助..提前谢谢..!!!

【问题讨论】:

  • 你为什么把new line放在邮递员的网址里?
  • 我想你不小心按下了Enter
  • @SomeDev 是的,谢谢..愚蠢的错误哈哈

标签: java spring-boot spring-security jwt


【解决方案1】:

看来你的路径是错误的。当您查看您的身体时,您会看到路径显示如下:/login%0A。这似乎您的 URL 末尾有一个额外的字符。只需尝试在 Postman 中重写 URL。

【讨论】:

    【解决方案2】:

    请考虑使用 BasicAuthenticationFilter 中的 shouldNotFilter 方法。它扩展了 OncePerRequestFilter,因此您可以在过滤类中使用它,如下所示:

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
           // code here
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-20
      • 2023-03-19
      • 2022-01-15
      • 2015-12-11
      • 2017-01-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-11
      • 2018-11-29
      相关资源
      最近更新 更多