spring-security 在JwtAuthenticationFilter之前执行一次请求过滤器

yshpjwxd  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(240)

我有一个设置,我需要在其中进行租户感知的身份验证和授权。

@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class TenantFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(TenantFilter.class);

    private static final String TENANT_HEADER = "X-Tenant"; 
    private static final String CONNECTION_STRING = "ObfuscatedConnectionString";
    private static final String TENANT_REPLACEMENT = "TENANT";

    @Override
    protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String dbConnectionString = CONNECTION_STRING.replace(TENANT_REPLACEMENT, "");
        ConnectionStorage.setConnection(dbConnectionString);
        filterChain.doFilter(request, response);
        ConnectionStorage.clear();
    }
}

虽然我知道有一些地方可以改进,但这只是一个占位符,以便能够测试该机制。2实际的实现将在后面进行。3当在不需要添加JWT的情况下进行测试时,它可以按预期工作,并且在给定正确的前提条件的情况下根据请求切换承租人。
但是,我不想在每个请求上都发送用户名+密码,而想在混合中添加一个JWT。

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    private AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter (AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication (HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        logger.info("Attempting authentication");
        try {
            UserRequest creds = new ObjectMapper()
                .readValue(request.getInputStream(), UserRequest.class);

            return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    creds.getUsername(),
                    creds.getPassword(),
                    new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication (HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException {
        logger.info("Authentication successful");
        String token = JWT.create()
            .withSubject(((User) auth.getPrincipal()).getUsername())
            .withExpiresAt(new Date(System.currentTimeMillis() + (JWTConstants.ACCESS_TOKEN_VALIDITY_SECONDS * 1000)))
            .sign(Algorithm.HMAC512(JWTConstants.SIGNING_KEY.getBytes()));
        response.addHeader(JWTConstants.HEADER_STRING, JWTConstants.TOKEN_PREFIX + token);
    }
}

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthorizationFilter.class);

    public JwtAuthorizationFilter (AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        logger.info("Doing filter internal");
        String header = request.getHeader(JWTConstants.HEADER_STRING);

        if (header == null || !header.startsWith(JWTConstants.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication (HttpServletRequest request) {
        String token = request.getHeader(JWTConstants.HEADER_STRING);
        if (token != null) {
            // parse the token.
            String user = JWT.require(Algorithm.HMAC512(JWTConstants.SIGNING_KEY.getBytes()))
                .build()
                .verify(token.replace(JWTConstants.TOKEN_PREFIX, ""))
                .getSubject();

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

}

为我的请求提供身份验证。在/api/user/login中有一个用户名和密码,还有一个jwt标记用于其他所有内容。
然而,看起来好像首先执行安全过滤器,并且我的承租人过滤器对于设置要转到的正确DB的connectionstring相当重要。
版本:springframework。 Boot :2.6.2

7d7tgy0s

7d7tgy0s1#

你可以看看这个答案:https://stackoverflow.com/a/59340951这将使您能够最大限度地控制过滤器的执行时间。
我想(但我可能错了)@Order(Ordered.HIGHEST_PRECEDENCE)也可能会起作用。虽然措辞有点奇怪,但Ordered.HIGHEST_PRECEDENCE会给你Integer.MIN_VALUE,较低的值会先执行。
顺便说一句,在编写您自己的JWT验证过滤器之前,还请查看Spring Security的OAuth 2.0功能(除非您有特定的情况):https://docs.spring.io/spring-security/reference/6.0.0-M1/servlet/oauth2/resource-server/jwt.html#oauth2resourceserver-jwt-minimalconfiguration

相关问题