【问题标题】:Read the already validated JWT token in a different micro service在不同的微服务中读取已经验证的 JWT 令牌
【发布时间】:2020-06-06 17:00:11
【问题描述】:

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

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

网关服务WebSecurity类配置方法

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 中启用此项,以便标头中的 jwt 令牌信息从网关传递给用户服务。

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

用户服务WebSecurity类配置方法

    @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

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 令牌的值并设置为安全上下文身份验证。我需要再写一个过滤器吗?该过滤器将来自何处。不应为注册或登录执行该过滤器。任何帮助将不胜感激。

【问题讨论】:

    标签: java spring-boot filter jwt jwt-auth


    【解决方案1】:

    您的第一个过滤器已根据您的代码设置为迎合/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: '*'

    【讨论】:

    • 感谢您的回复。我会试试这个。同时,我如何确保仅对未登录或未注册请求请求执行第二个过滤器,即标头中存在 jwt 令牌。与注册或登录的情况一样,标头中不会出现 jwt 令牌。
    • 检查this。你的过滤器,它扩展了 UsernamePasswordAuthenticationFilter 将首先出现。
    • 我能够做到这一点。但是一个新问题显然是 jwt 令牌没有从网关传递到用户服务,即使我启用了 zuul.routes.auth-service.sensitive-headers=Cookie,Set-Cookie
    • 除了敏感头,还需要加上ignoredServices: '*'
    猜你喜欢
    • 2018-02-24
    • 2017-12-29
    • 2021-09-30
    • 2017-08-18
    • 1970-01-01
    • 2021-08-13
    • 2016-11-22
    • 2022-01-11
    • 2018-10-12
    相关资源
    最近更新 更多