Java 如何在Spring Security中从筛选器向标头添加值?

Java 如何在Spring Security中从筛选器向标头添加值?,java,json,spring,spring-mvc,spring-security,Java,Json,Spring,Spring Mvc,Spring Security,我正在spring security和spring session中使用JWT开发一个web服务。我的意图是添加一个过滤器来验证JWT,提取其JTI并将其作为“x-auth-token”添加到头服务中。Json Web令牌JTI与新用户通过身份验证时生成spring会话的“会话\u id”相匹配(可以通过RequestContextHolder.currentRequestAttributes().GetSessionId()查看)。当用户通过身份验证时,这被放入Json Web令牌JTI中 我

我正在spring security和spring session中使用JWT开发一个web服务。我的意图是添加一个过滤器来验证JWT,提取其JTI并将其作为“x-auth-token”添加到头服务中。Json Web令牌JTI与新用户通过身份验证时生成spring会话的“会话\u id”相匹配(可以通过
RequestContextHolder.currentRequestAttributes().GetSessionId()
查看)。当用户通过身份验证时,这被放入Json Web令牌JTI中

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

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

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);

    }
}
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);
    }

}
@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);

    }
 .addFilterAfter (getCustomFilter (),
 UsernamePasswordAuthenticationFilter.class) 
 .addFilterAfter
 (getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)
@Bean
    public CustomFilter getCustomFilter(){
        return new CustomFilter();
    }

    @Bean
    public CustomFilter2 getCustomFilter2(){
        return new CustomFilter2();
    }
这样,spring会话令牌将不会由用户插入,而是在从JWT中提取之后由过滤器插入

我尝试通过一个从HttpServletRequestWrapper扩展而来的类来实现,如下所示:

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);

    }
}
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);
    }

}
@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);

    }
 .addFilterAfter (getCustomFilter (),
 UsernamePasswordAuthenticationFilter.class) 
 .addFilterAfter
 (getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)
@Bean
    public CustomFilter getCustomFilter(){
        return new CustomFilter();
    }

    @Bean
    public CustomFilter2 getCustomFilter2(){
        return new CustomFilter2();
    }
然而,它不起作用,我不知道我是否错过了什么或是不是这样做的方式

编辑2017年10月2日:

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

如果我在头中从头开始发送令牌(x-auth-token),一切正常

编辑2017年10月5日:

我已经创建了第二个过滤器来检查头中第一个文件管理器添加的值。第一个过滤器没有从ServletRequest接收值“x-auth-token”,而是将其添加为“requestWrapper”

第二个筛选器添加到配置类中,如下所示:

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);

    }
}
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);
    }

}
@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);

    }
 .addFilterAfter (getCustomFilter (),
 UsernamePasswordAuthenticationFilter.class) 
 .addFilterAfter
 (getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)
@Bean
    public CustomFilter getCustomFilter(){
        return new CustomFilter();
    }

    @Bean
    public CustomFilter2 getCustomFilter2(){
        return new CustomFilter2();
    }
其中getCustomFilter()和getCustomFilter2()是使用如下bean创建的:

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);

    }
}
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);
    }

}
@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);

    }
 .addFilterAfter (getCustomFilter (),
 UsernamePasswordAuthenticationFilter.class) 
 .addFilterAfter
 (getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)
@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会话的执行顺序

在我们的筛选器之后,如何调用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();
     }

}

您是否已验证筛选器的顺序,以确保此筛选器按正确的顺序执行

更新以添加更多信息:

查看spring文档:

您可以通过添加注释
@enableRedistpSession
来启用spring会话

@enableRedistpSession注释创建了一个Springbean,其中 实现筛选器的springSessionRepositoryFilter的名称。这个 filter负责替换HttpSession 由Spring会话支持的实现。在这种情况下,是春天 会话由Redis支持

据此,springSessionRepositoryFilter是springSessionRepositoryFilter的一个实例:

RedisHttpSessionConfiguration将SessionRepositoryFilter公开为名为 “springSessionRepositoryFilter”。要使用此选项,请使用单个 RedisConnectionFactory必须作为Bean公开

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

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

我们可以通过在
过滤器中添加属性来实现这一点

setAttribute(“键名”、“值”)


Controller
中,我们可以通过
@RequestAttribute

Hello mad\u fox访问它们。该服务有两个筛选器,第一个用于验证并生成JWT,第二个用于稍后验证(名为
CustomFilter
)。但是,根据您的评论,我所做的是在获得JWT后禁用第一个过滤器,这样就不用使用验证过滤器(
CustomFilter
)。问题是,当启动时
x-auth-token
值不在头中时,spring无法识别它,即使我使用
requestWrapper.addHeader()
添加它。我添加了以下过滤器:
.addFilterAfter(new CustomFilter(),UsernamePasswordAuthenticationFilter.class)
在扩展了
websecurityConfigureAdapter
Ey mad_fox的配置类中,您是对的,错误在于顺序。我用两个自定义过滤器进行了测试。第一个过滤器使用HeaderPrequestWrapper添加x-auth-token值,第二个过滤器仅从请求中读取该值。它工作得很好。但它继续向我发送筛选器以验证新用户。就像你说的顺序不对。春季课程在其他时间开始。您知道如何在spring会话开始前调用筛选器吗?您能更新您的问题以包括添加筛选器的文件吗?然后,我可以提供有关如何设置排序的更多信息。我询问您是否可以发布包含以下代码的类:.addFilterAfter(getCustomFilter(),UsernamePasswordAuthenticationFilter.class)。addFilterAfter(getCustomFilter2(),UsernamePasswordAuthenticationFilter.class)这对我来说还不足以提供解决方案。编辑2012年10月2日?!