I am developing a web service with JWT in spring security and spring session. My intention is to add a filter that validates the JWT, extract its JTI and add it in the header service as "x-auth-token". The Json Web Token JTI matches the "Session_id" that generates spring session when a new user is authenticated (it can see with RequestContextHolder.currentRequestAttributes().GetSessionId()). This was put in the Json Web Token JTI when the user was authenticated.
I already have the filter that validates the JWT, however, I will not put it here now for simplicity. What I will put will be only the filter class with the written method doFilter.
What I'm trying to do is add a value to the header, something like this:
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);
}
}
This way the spring session token will not be inserted by the user, but by the filter once it is extracted from the JWT.
I have tried to do it through a class that extends from HttpServletRequestWrapper, like this:
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);
}
}
and defining the doFilter method like this:
@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);
}
However it does not work, I do not know if I have missed something or is not the way to do it.
Edit 02/10/2017:
When I run the service spring recognizes that there is no token (x-auth-token) in the header and automatically sends me the filter to authenticate the new user, which causes a Forbidden error because there is no user and password.
If I send the token (x-auth-token) from the beginning in the header everything works fine.
Edit 05/10/2017:
I have created an second filter to check the value that was added by the first filer in the header. The first filter does not receive the value "x-auth-token" from the ServletRequest, it adds it with "requestWrapper".
The second filter was added to configuration class like this:
.addFilterAfter (getCustomFilter (),
UsernamePasswordAuthenticationFilter.class)
.addFilterAfter
(getCustomFilter2 (), UsernamePasswordAuthenticationFilter.class)
where getCustomFilter () and getCustomFilter2 () were created using a bean like this:
@Bean
public CustomFilter getCustomFilter(){
return new CustomFilter();
}
@Bean
public CustomFilter2 getCustomFilter2(){
return new CustomFilter2();
}
The second filter is defined as follows:
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
}
}
When I run the service spring recognizes that there is no token (x-auth-token) in the original header and automatically sends me the filter to authenticate the new user.
I think the problem is the order in which spring session is executed.
How can I call spring session after our filters?
The configuration class is the follows:
@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();
}
}