在不同的微服务中读取已经验证的 JWT 令牌

Read the already validated JWT token in a different micro service

我有两个微服务,即网关服务和用户服务。网关服务将登录调用重定向到用户服务。用户服务将生成包含角色和权限等信息的 jwt 令牌,并将其附加到响应 header.

现在客户端可以调用用户服务来创建用户。在请求中传递 jwt 令牌,网关服务验证令牌并将调用转发给用户服务。由于我需要检查登录用户是否具有写入权限(基于@PreAuthorize),我需要在用户服务中再次读取 JWT 令牌并提取角色和权限。

网关服务WebSecurityclass配置方法

protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.headers().frameOptions().disable();

        http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/user/signup").permitAll()
        .antMatchers(HttpMethod.POST, "/user/signin").permitAll()
        .anyRequest().authenticated()
        .and()
        .addFilter(new AuthenticationFilter(authenticationManager(), environment));


        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

AuthenticationFilter.Class

public class AuthenticationFilter extends BasicAuthenticationFilter{

    private final Environment environment;

    public AuthenticationFilter(AuthenticationManager authenticationManager, Environment environment) {
        super(authenticationManager);

        this.environment = environment;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));

        if (authHeader == null || !authHeader.startsWith("Bearer")) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));

        //validate jwt token code
    }

application.properties 中启用此功能,以便 header 中的 jwt 令牌信息从网关传递到用户服务。

zuul.routes.user-service.sensitive-headers=Cookie,Set-Cookie

用户服务网络安全class配置方法

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("api.gateway.ip"))
        .and()
        .addFilter(getAuthenticationFilter());
        http.headers().frameOptions().disable();
    }

    private AuthenticationFilter getAuthenticationFilter() throws Exception{
                AuthenticationFilter filter = new AuthenticationFilter(usersService, environment, authenticationManager());
        filter.setFilterProcessesUrl("/signin");
        return filter;
    }

UserService AuthenticationFilter class

public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationService userService;
    private final Environment environment;

    @Autowired
    public AuthenticationFilter(AuthenticationService userService, Environment environment, AuthenticationManager authManager) {
        this.userService = userService;
        this.environment = environment;
        super.setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        try {
            LoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(), LoginRequestModel.class);

            return getAuthenticationManager().authenticate(
                        new UsernamePasswordAuthenticationToken(creds.getEmail(), creds.getPasssword(), new ArrayList<>())
                    );
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        //generate jwt token code
    }
}

现在如何读取网关服务发送的 jwt 令牌的值并设置为安全上下文身份验证。我需要再写一个过滤器吗?该过滤器将来自何处。不应为注册或登录执行该过滤器。任何帮助将不胜感激。

根据您的代码,您的第一个过滤器设置为满足 /signin

filter.setFilterProcessesUrl("/signin");

现在,您需要第二个过滤器来满足其他所有需求,例如:

.and()
.addFilter(getAuthenticationFilter())
.addFilter(getAuthorizationFilter());

...

@Bean
public AuthorizationFilter getAuthorizationFilter(){
    AuthorizationFilter a = new AuthorizationFilter(authenticationManager());
    a.setSecret(secret);

    return a;
}

例如,您可以:

public class AuthorizationFilter extends BasicAuthenticationFilter {
    ....
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
        FilterChain chain) throws IOException, ServletException {

        //try/catch

        String jwt = getJWT(r);

        if(jwt != null){
            Authentication a = getAuthentication(jwt);

            if(a != null){
                SecurityContextHolder.getContext().setAuthentication(a);
            }
        }

        chain.doFilter(req, res);
    }

    private String getJWT(HttpServletRequest r){
        String bearerToken = r.getHeader("Authorization");

        //Do your checks here

        return ...;
    }

    private Authentication getAuthentication(String jwt){
        //Parse the jwt etc

        return new UsernamePasswordAuthenticationToken(...);
    }
}

关于Zuul,还需要补充:ignoredServices: '*'