在其他微服务中读取已验证的JWT令牌

时间:2020-06-06 05:37:38

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

我有两个微服务,即网关服务和用户服务。网关服务将登录调用重定向到用户服务。用户服务将生成具有角色和权限等信息的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令牌的值并将其设置为安全上下文身份验证。我是否需要再编写一个过滤器。该过滤器将到达何处。该过滤器不应用于注册或登录。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分: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: '*'