Spring Security:为什么要检查授权过滤器中是否设置了身份验证令牌?

时间:2021-04-11 20:19:04

标签: spring spring-boot spring-security jwt

我正在尝试了解 Spring 安全性。我遇到了以下代码

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    public JWTAuthorizationFilter(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);
    }

    // Reads the JWT from the Authorization header, and then uses JWT to validate the token
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);

        if (token != null) {
            // parse the token.
            String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
                    .build()
                    .verify(token.replace(TOKEN_PREFIX, ""))
                    .getSubject();

            if (user != null) {
                // new arraylist means authorities
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }

            return null;
        }

        return null;
    }

需要什么

if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            chain.doFilter(req, res);
            return;
        }

认证后会进行授权吗?在那种情况下,无论如何都会设置标题。我错过了什么? 还有 chain.doFilter() 到底在做什么?它用于继续并最终命中 servlet,对吗?如果用户未通过身份验证,为什么要继续请求?如果要进行身份验证过滤器,那么如何在身份验证过滤器之前调用授权过滤器?

1 个答案:

答案 0 :(得分:0)

在 Spring Security 中,过滤器链基本上是一个链表。因此,当您发出请求时,它将命中第一个过滤器,然后该过滤器将调用链接中的下一个链,然后是下一个、下一个等。

// Get a specific header (im guessing authorization header)
String header = req.getHeader(HEADER_STRING);

// If no such header was found, or the found header does not include a specific prefix 
// (im guessing the string Baerer) which means that if this is not a
// authentication header containing a baerer token, then call the next filter in 
// the chain.. The next filter will have the same call, etc.
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
    chain.doFilter(req, res);
    return;
}

如果发送的请求没有 auth 标头,我们继续进入应用程序,当我们到达实际端点时,沿着过滤器链向下,它可能会有一个注释说您需要一个特定的角色,而用户不会拥有该角色,因为它没有对自己进行身份验证。结果将是安全框架返回的 401 UNAUTHORIZED。

另一方面,如果它找到一个带有 bearer 前缀的 auth 标头,它将尝试从标头中提取令牌,验证其完整性以确保没有人篡改过它,对其进行解码,然后得到令牌中的 subject 字段。使用主题,它将创建一个 UsernamePasswordAuthenticationToken 并将其设置为安全上下文中的经过身份验证的令牌。基本上将 subject 设置为经过身份验证的用户。

如果令牌中没有主题,过滤器将返回 null(这是草率的编码,可能会导致应用程序崩溃,应该抛出 401 异常)。

这段代码有几个不好的地方:

  • String header = req.getHeader(HEADER_STRING); 被调用两次
  • 默认用户案例是尝试登录用户,应该反过来,默认案例应该是抛出 401 未经授权,所有逻辑都应尝试阻止这种情况发生。
  • 如果包含字符串 authorizationbearer 标头但没有发送标记,解析函数将返回一个空值,过滤器将调用 setAuthentication 的值为 {{1} } 这可能会导致崩溃。
  • 如果包含字符串 nullauthorization 标头不包含 bearer 值,解析函数将返回空值,过滤器将调用 subjectsetAuthentication 可能会导致崩溃。
  • Spring 已经包含对 null jwt 库的支持,以对 JWT 进行编码和解码,因此无需像上面的示例中那样引入另一个库 Nimbus

值得一提的是,从 Spring 5 开始,Spring Security 中已经有一个 fully implemented JWT solution 只需要自定义即可。所以整个自定义过滤器是多余的。