如何从Spring Security中的过滤器向标头添加值?

时间:2017-10-01 16:48:54

标签: java json spring spring-mvc spring-security

我正在春季安全和春季会议中使用JWT开发Web服务。我的目的是添加一个验证JWT的过滤器,提取其JTI并将其添加到标题服务中作为" x-auth-token"。 Json Web Token JTI匹配" Session_id"在新用户通过身份验证时生成spring会话(可以使用RequestContextHolder.currentRequestAttributes().GetSessionId()查看)。当用户通过身份验证时,这被放入Json Web Token JTI。

我已经有了验证JWT的过滤器,但是,为了简单起见,我现在不会把它放在这里。我将放置的只是具有写入方法doFilter的过滤器类。

我尝试做的是在标题中添加一个值,如下所示:

public class CustomFilter extends GenericFilterBean {

    @Override
    public void doFilter(
      ServletRequest request, 
      ServletResponse response,
      FilterChain chain) throws IOException, ServletException {

        /*
         * In this part I validate the token and extract the JTI, which is equal to the session_id of spring session.
         * Suppose that JTI = 71b0b8c1-1eac-46ce-80b6-f14c2e08c0de
         */

        //I want to do something like this:
        request.addHeader("x-auth-token", "71b0b8c1-1eac-46ce-80b6-f14c2e08c0de");

        chain.doFilter(request, response);

    }
}

这样,弹出会话令牌不会被用户插入,而是在从JWT中提取后由过滤器插入。

我试图通过一个从HttpServletRequestWrapper扩展的类来完成它,如下所示:

public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {
    /**
     * construct a wrapper for this request
     * 
     * @param request
     */
    public HeaderMapRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    private Map<String, String> headerMap = new HashMap<String, String>();

    /**
     * add a header with given name and value
     * 
     * @param name
     * @param value
     */
    public void addHeader(String name, String value) {
        headerMap.put(name, value);
    }

    @Override
    public String getHeader(String name) {
        String headerValue = super.getHeader(name);
        if (headerMap.containsKey(name)) {
            headerValue = headerMap.get(name);
        }
        return headerValue;
    }

    /**
     * get the Header names
     */
    @Override
    public Enumeration<String> getHeaderNames() {
        List<String> names = Collections.list(super.getHeaderNames());
        for (String name : headerMap.keySet()) {
            names.add(name);
        }
        return Collections.enumeration(names);
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        List<String> values = Collections.list(super.getHeaders(name));
        if (headerMap.containsKey(name)) {
            values.add(headerMap.get(name));
        }
        return Collections.enumeration(values);
    }

}

并像这样定义doFilter方法:

@Override
    public void doFilter(
      ServletRequest request, 
      ServletResponse response,
      FilterChain chain) throws IOException, ServletException {

        /*
             * In this part I validate the token and extract the JTI, which is equal to the session_id of spring session.
             * Suppose that JTI = 71b0b8c1-1eac-46ce-80b6-f14c2e08c0de
             */

        HttpServletRequest r = (HttpServletRequest) request;

        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(r);

        requestWrapper.addHeader("x-auth-token", "71b0b8c1-1eac-46ce-80b6-f14c2e08c0de");

        chain.doFilter(requestWrapper, response);

    }

然而它不起作用,我不知道我是否遗漏了某些东西或者不是这样做的。

编辑02/10/2017:

当我运行服务时,spring认识到标头中没有令牌(x-auth-token),并自动向我发送过滤器以验证新用户,这会导致Forbidden错误,因为没有用户和密码。

如果我从标题的开头发送令牌(x-auth-token),一切正常。

编辑05/10/2017:

我创建了第二个过滤器来检查标头中第一个文件管理器添加的值。第一个过滤器没有收到值&#34; x-auth-token&#34;从ServletRequest中,它添加了&#34; requestWrapper&#34;。

第二个过滤器已添加到配置类中,如下所示:

 .addFilterAfter (getCustomFilter (),
 UsernamePasswordAuthenticationFilter.class) 
 .addFilterAfter
 (getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)

其中getCustomFilter()和getCustomFilter2()是使用这样的bean创建的:

@Bean
    public CustomFilter getCustomFilter(){
        return new CustomFilter();
    }

    @Bean
    public CustomFilter2 getCustomFilter2(){
        return new CustomFilter2();
    }

第二个过滤器定义如下:

public class CustomFilter2  implements Filter {
    @Override
    public void doFilter(
      ServletRequest request, 
      ServletResponse response,
      FilterChain chain) throws IOException, ServletException {     

        HttpServletRequest req = (HttpServletRequest) request;

        System.out.println("Result: " + req.getHeader("x-auth-token"));
        chain.doFilter(request, response);

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }
}

当我运行服务时,spring认识到原始标头中没有令牌(x-auth-token),并自动向我发送过滤器以验证新用户。

我认为问题在于春季会话的执行顺序。

如何在过滤后调用spring会话?

配置类如下:

@Configuration
@EnableWebSecurity 
public class SeguridadConfiguracion extends  WebSecurityConfigurerAdapter {

    @Autowired 
    @Qualifier("seguridadServicio")
    private UserDetailsService objSeguridadServicio;

    @Autowired 
    public void configure(AuthenticationManagerBuilder auth) throws Exception { 
        auth.userDetailsService(objSeguridadServicio); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
            http
              .authorizeRequests() 
                         .antMatchers("/**").hasAnyAuthority("ComisionadoSI","GerenciaSI")
                         .anyRequest().authenticated()
                         .and() 
             .logout().clearAuthentication(true)
                      .invalidateHttpSession(true)
                      .and()
             .formLogin()
                        .and()
             .httpBasic() 
                        .and()

             .addFilterAfter(getCustomFilter(), UsernamePasswordAuthenticationFilter.class)
             .addFilterAfter(getCustomFilter2(), UsernamePasswordAuthenticationFilter.class)

             .csrf().disable();

    }

    @Bean
    public CustomFilter getCustomFilter(){
        return new CustomFilter();
    }

    @Bean
    public CustomFilter2 getCustomFilter2(){
        return new CustomFilter2();
    }

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
     }

}

2 个答案:

答案 0 :(得分:1)

我们可以通过filter中的addingAttribute来实现

httpServletRequest.setAttribute(“键名”,“值”);

Controller中,我们可以通过@RequestAttribute来访问它们

答案 1 :(得分:0)

您是否验证了过滤器的顺序,以确保以正确的顺序执行此过滤器?

更新以添加更多信息:

查看春季文档:https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-rest

通过添加注释@EnableRedisHttpSession启用spring会话。

  

@EnableRedisHttpSession注释创建一个带有的Spring Bean   实现Filter的springSessionRepositoryFilter的名称。该   filter是负责替换HttpSession的   实现由Spring Session支持。在这个例子中Spring   会话由Redis支持。

根据这个,springSessionRepositoryFilter是springSessionRepositoryFilter的一个实例:https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.html

  

RedisHttpSessionConfiguration将SessionRepositoryFilter公开为名为bean的bean   &#34; springSessionRepositoryFilter&#34 ;.为了使用这个单一的   RedisConnectionFactory必须作为Bean公开。

基于此,我认为您需要在SessionRepositoryFilter之前添加过滤器,如:

.addFilterAfter(getCustomFilter(), SessionRepositoryFilter.class)
.addFilterAfter(getCustomFilter2(), SessionRepositoryFilter.class)