Reputation: 4007
I am securing my REST api using Basic-Auth. On correct credentials passed by user, a controller is responsible for sending a httpOnly
and secure
cookie in response.
@GetMapping
@ResponseStatus(value=HttpStatus.OK)
public void loginUser( final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {
setAuthCookieToResonse(request,response);
}
private void setAuthCookieToResonse(final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {
String cookieKey = "auth";
String cookieValue = request.getHeader("Authorization");
if (cookieValue != null) {
Cookie cookie = new Cookie(cookieKey, cookieValue);
cookie.setHttpOnly(true);
cookie.setSecure(true);
response.addCookie(cookie);
}
}
So, now with each request a cookie is being sent by the browser, which will contain Basic-Auth details. But the problem is, how do the spring security extract those credentials from that cookie?
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {// @formatter:off
httpSecurity
.cors()
.and().authorizeRequests()
.antMatchers("/signup/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
;
}
My guess was:
To add a filter before BasicAuthenticationFilter.class
and extract the credentials from cookie
and than add those credentials to the HttpServletRequest
's Authorizaton
header which is going to be passed to spring-security layer. But the problem is, HttpServletRequest
doesn't have API to add headers.
What would be the right way to implement this?
Upvotes: 0
Views: 13203
Reputation: 4007
I made this working after following this blog (archived). But I would love to hear other solutions, especially using some spring configuration itself. Spring is a very matured framework, it must(should) have something to handle this common problem.
Since, the HttpServletRequest
don't have any method to add the new headers, I need to create a custom class which can add new headers to the request, this can be achived by HttpServletRequestWrapper
. Here is the implementation.
public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
// holds custom header and value mapping
private final Map<String, String> customHeaders;
public MutableHttpServletRequest(HttpServletRequest request) {
super(request);
this.customHeaders = new HashMap<String, String>();
}
public void putHeader(String name, String value) {
this.customHeaders.put(name, value);
}
public String getHeader(String name) {
// check the custom headers first
String headerValue = customHeaders.get(name);
if (headerValue != null) {
return headerValue;
}
// else return from into the original wrapped object
return ((HttpServletRequest) getRequest()).getHeader(name);
}
public Enumeration<String> getHeaderNames() {
// create a set of the custom header names
Set<String> set = new HashSet<String>(customHeaders.keySet());
// now add the headers from the wrapped request object
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
// add the names of the request headers into the list
String n = e.nextElement();
set.add(n);
}
// create an enumeration from the set and return
return Collections.enumeration(set);
}
}
The filter which checks for the cookie, before the Spring-secuirty:
public class CheckAuthCookieFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(httpServletRequest);
Cookie[] cookies = httpServletRequest.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
logger.debug(cookie.getName() + " : " + cookie.getValue());
if (cookie.getName().equalsIgnoreCase("auth")) {
mutableRequest.putHeader("Authorization", URLDecoder.decode(cookie.getValue(), "utf-8"));
}
}
}
chain.doFilter(mutableRequest, response);
}
}
and finally the security configuration:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {// @formatter:off
httpSecurity
.cors()
.and().authorizeRequests()
.antMatchers("/signup/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
;
httpSecurity.addFilterBefore(new CheckAuthCookieFilter(), BasicAuthenticationFilter.class);
}
My custom filter will run before the Spring's BasicAuthenticationFilter
.If there is a cookie present with name auth
(which the application created on successful login), than that's the cookie which holds the basic auth credentials. The credentials are extracted from that, and added to the header of request
. Then the BasicAuthenticationFilter
will run and look for the Authorization
and proceed with its normal flow.
Upvotes: 3