3

我继承了一个写了一半的 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 标头,我们不应该阻止请求吗?
4

0 回答 0