spring-security - JwtUsernameAndPasswordAuthenticationFilter 在这个例子中是如何工作的?
问题描述
下面是我的配置示例,据我所知,JwtUsernameAndPasswordAuthenticationFilter 首先工作(它从请求中获取用户名、密码,检查它们是否正确并提供令牌),然后是 - JwtTokenVerifier。
我有几个问题:
JwtUsernameAndPasswordAuthenticationFilter 是否每次都检查请求是否包含用户名和密码?如果没有,什么时候检查?一次什么?
为什么我们在没有密码的 JwtTokenVerifier 类中创建 Authentication 对象(只有用户名和 Authorities )并将其放入 Context 中?
PS我非常感谢你的回答!并且知道这个问题看起来有多愚蠢。
@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);
}
}
解决方案
JwtUsernameAndPasswordAuthenticationFilter
extendsUsernamePasswordAuthenticationFilter
,默认情况下,当您对“/login”进行“POST”调用时会触发它。这可以通过setFilterProcessesUrl(<PATH_HERE>)
在JwtUsernameAndPasswordAuthenticationFilter
的构造函数中调用来改变。您不会仅仅因为不需要密码而将密码保存在 Authentication 对象中,而且将密码保存在内存之外更安全,因为任何有权访问内存转储的人都可以检索密码。
推荐阅读
- python - 在 Python 中使用 super() 进行多重继承
- python - 如何在机器学习中对包含(假设超过 200 个)分类值的列进行编码?
- sql - 使用 Oracle 中的条件更新表
- rust - 如果 Rust 项目已更新,则重新运行规则
- python-3.x - html和python与flask的集成
- sql - 创建一个变量来存储在公司工作超过 3 年的员工人数
- ngrx - ngrx/data 无法让 selectId 使用 Id 而不是 id
- javascript - 保护访问令牌免受 XSS 攻击
- node.js - request.body.name 在 expressjs 应用程序中不起作用
- forms - 我需要在 a 中展示产品。落下