我正在春季安全和春季会议中使用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();
}
}
答案 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)