我继承了一个写了一半的 Spring Boot REST 服务,它使用 Spring Sec 来实现基于 JWT 的 API 身份验证。Gradle 安全相关的依赖项是:
'org.springframework.security:spring-security-jwt:1.0.9.RELEASE'
'org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE'
'io.jsonwebtoken:jjwt:0.9.0'
'org.springframework.boot:spring-boot-starter-security'
这个应用程序使用 Spring Sec 过滤器来实现整个身份验证解决方案,我正试图了解它是如何工作的,而对于我的生活来说,一些关键的事情是无法理解的:-/
这是代码:
public class MyAppAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public MyAppAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
public class MyAppAuthorizationFilter extends BasicAuthenticationFilter {
public MyAppAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AccountDAO accountDAO;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountDAO.findByUsername(username);
if(account == null) {
throw new UsernameNotFoundException(username);
}
return new User(account.username, account.password, []);
}
}
我不明白的是:
- 我可以假设 Spring Security 自动以正确的顺序定位这些过滤器吗?那就是:
MyAppAuthenticationFilter
总是在MyAppAuthorizationFilter
? - 我真的被
authenticationManager.authenticate(...)
里面的电话弄糊涂了MyAppAuthenticationFilter#attemptAuthentication
。如何creds.getUsename()
与cred.getPassword()
存储在数据库(或 LDAP 或其他任何地方)中的用户信息进行比较?这个机制有什么关系UserDetailsServiceImpl#loadByUsername(String)
? - 所有的逻辑
MyAppAuthorizationFilter#doFilterInternal
对我来说都没有意义。对我来说,我将其解读为:检查请求中是否有 JWT 令牌标头。如果没有,请继续以任何方式提出请求(!!!!)。如果有,则继续检查 JWT 是否有一个有效用户作为其主题。如果请求中没有 JWT 标头,我们不应该阻止请求吗?