【问题标题】:How does JwtUsernameAndPasswordAuthenticationFilter work in this example?JwtUsernameAndPasswordAuthenticationFilter 在这个例子中是如何工作的?
【发布时间】:2021-10-05 23:39:47
【问题描述】:

下面是我的配置示例,据我所知,JwtUsernameAndPasswordAuthenticationFilter 首先工作(它从请求中获取用户名、密码,检查它们是否正确并提供令牌),然后 - JwtTokenVerifier。

我有几个问题:

  1. JwtUsernameAndPasswordAuthenticationFilter 是否每次都检查请求是否包含用户名和密码?如果没有,什么时候检查?一次什么?

  2. 为什么我们在 JwtTokenVerifier 类中创建没有密码(只有用户名和权限)的 Authentication 对象并将其放入 Context 中?

附:我很感激你的回答!并且知道这个问题看起来有多愚蠢。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, secretKey))
                .addFilterAfter(new JwtTokenVerifier(secretKey, jwtConfig),JwtUsernameAndPasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers("/", "index", "/css/*", "/js/*").permitAll()
                .antMatchers("/api/**").hasRole(STUDENT.name())
                .anyRequest()
                .authenticated();
    }


public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final JwtConfig jwtConfig;
    private final SecretKey secretKey;

    public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authenticationManager,
                                                      JwtConfig jwtConfig,
                                                      SecretKey secretKey) {
        this.authenticationManager = authenticationManager;
        this.jwtConfig = jwtConfig;
        this.secretKey = secretKey;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        try {
            UsernameAndPasswordAuthenticationRequest authenticationRequest = new ObjectMapper()
                    .readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class);

            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    authenticationRequest.getUsername(),
                    authenticationRequest.getPassword()
            );

            Authentication authenticate = authenticationManager.authenticate(authentication);
            return authenticate;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        String token = Jwts.builder()
                .setSubject(authResult.getName())
                .claim("authorities", authResult.getAuthorities())
                .setIssuedAt(new Date())
                .setExpiration(java.sql.Date.valueOf(LocalDate.now().plusDays(jwtConfig.getTokenExpirationAfterDays())))
                .signWith(secretKey)
                .compact();

        response.addHeader(jwtConfig.getAuthorizationHeader(), jwtConfig.getTokenPrefix() + token);
    }
}

public class JwtTokenVerifier extends OncePerRequestFilter {

private final SecretKey secretKey;
private final JwtConfig jwtConfig;

public JwtTokenVerifier(SecretKey secretKey,
                        JwtConfig jwtConfig) {
    this.secretKey = secretKey;
    this.jwtConfig = jwtConfig;
}

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

    String authorizationHeader = request.getHeader(jwtConfig.getAuthorizationHeader());

    if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
        filterChain.doFilter(request, response);
        return;
    }

    String token = authorizationHeader.replace(jwtConfig.getTokenPrefix(), "");

    try {

        Jws<Claims> claimsJws = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token);

        Claims body = claimsJws.getBody();

        String username = body.getSubject();

        var authorities = (List<Map<String, String>>) body.get("authorities");

        Set<SimpleGrantedAuthority> simpleGrantedAuthorities = authorities.stream()
                .map(m -> new SimpleGrantedAuthority(m.get("authority")))
                .collect(Collectors.toSet());

        Authentication authentication = new UsernamePasswordAuthenticationToken(
                username,
                null,
                simpleGrantedAuthorities
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

    } catch (JwtException e) {
        throw new IllegalStateException(String.format("Token %s cannot be trusted", token));
    }

    filterChain.doFilter(request, response);
}

}

【问题讨论】:

    标签: spring-security jwt


    【解决方案1】:
    1. JwtUsernameAndPasswordAuthenticationFilter 扩展UsernamePasswordAuthenticationFilter,默认情况下,当您对“/login”进行“POST”调用时会触发它。这可以通过在JwtUsernameAndPasswordAuthenticationFilter 的构造函数中调用setFilterProcessesUrl(&lt;PATH_HERE&gt;) 来更改。

    2. 您不会仅仅因为不需要密码而将密码保存在 Authentication 对象中,而且将密码保存在内存之外更安全,因为任何有权访问内存转储的人都可以检索密码。

    【讨论】:

      猜你喜欢
      • 2021-10-05
      • 2017-11-26
      • 2021-10-14
      • 1970-01-01
      • 2014-10-22
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 2020-07-17
      相关资源
      最近更新 更多