Java 如何在Spring Security中从筛选器向标头添加值?
我正在spring security和spring session中使用JWT开发一个web服务。我的意图是添加一个过滤器来验证JWT,提取其JTI并将其作为“x-auth-token”添加到头服务中。Json Web令牌JTI与新用户通过身份验证时生成spring会话的“会话\u id”相匹配(可以通过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中 我
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日?!