How to disable Session cookie in Apache HttpAsyncClientBuilder - apache-httpcomponents

I'm talking to a service that fails in getting the user authentication cookie if there is a JSESSIONID cookie in the request, and I can't modify this service.
It also returns this session cookie on each response, so my first request work (no other cookie than the user's one), but next requests will always fail.
My restTemplate configuration uses a custom request factory that extends Spring's HttpComponentsAsyncClientHttpRequestFactory with an AsyncClient from Apache's HttpAsyncClientBuilder.
Is there a way to configure that to always ignore the session cookie ?
Thanks in advance!

It would have been nice to find a solution impliying only configuration, but I couldn't so I ended up extending the BasicCookieStore:
public class DefaultCookieStore extends BasicCookieStore {
private static String SESSION_COOKIE_NAME = "JSESSIONID";
#Override
public void addCookie(Cookie cookie) {
if (!SESSION_COOKIE_NAME.equals(cookie.getName())) {
super.addCookie(cookie);
}
}
}
And adding it to my HttpAsyncClientBuilder and HttpClientBuilder with the method setDefaultCookieStore.
Probably not the best thing, but it works well.

Related

set Domain on cookie using spring security when login success

How can I set the property "domain" on the users cookie when the user has authenticated from spring?
Edit: id like to add domain=".mydomain.com" to cookie with id JSESSIONID
I dont want to deal with spring-session-core or the particular implementation of the session like redis, and Im not using spring-boot. What is the easiest way to do this?
I dont want to jump in the rabbit hole of redis if I can avoid it.
Edit: investigated if set_cookie can be modified in custom implementation of AuthenticationSuccessHandlerImpl that extends AbstractAuthenticationTargetUrlRequestHandler, but "set_cookie" isnt set until
response.sendRedirect(redirectUrl);
of DefaultRedirectStrategy implements RedirectStrategy, but the also isCommitted()==True so set_cookie cant be changed.
I varified this by implementing my redirect strategy:
#Override
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, java.lang.String url)
throws IOException {
LOGGER.info("sendRedirect cookie size: "+response.getHeaders(HttpHeaders.SET_COOKIE).size()+ " is commited:"+response.isCommitted());
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
LOGGER.info("sendRedirect cookie size: "+response.getHeaders(HttpHeaders.SET_COOKIE).size()+ " is commited:"+response.isCommitted());
response.sendRedirect(redirectUrl);
}
Looks like set_cookie is set in response.sendRedirect and is committed at the same time.

Spring RSocket over WebSocket - Access user information from HTTP session

In my web application, users login using a username/password combination and get a session cookie. When initiating a WebSocket connection, I can easily access the user information in the WebSocketHandler, for example:
#Component
public class MyWebSocketHandler implements WebSocketHandler {
#Override
public Mono<Void> handle(WebSocketSession session) {
// two ways to access security context information, either like this:
Mono<Principal> principal = session.getHandshakeInfo().getPrincipal();
// or like this
Mono<SecurityContext> context = ReactiveSecurityContextHolder.getContext();
//...
return Mono.empty();
}
}
Both reuse the HTTP session from the WebSocket handshake, I don't have to send additional authentication over the WebSocket itself. With STOMP the same thing applies: I can just reuse the information of the HTTP session.
How do I achieve the same thing using RSocket? For example, how would I get information about the user inside a MessageMapping method like this?:
#Controller
public class RSocketController {
#MessageMapping("test-stream")
public Flux<String> streamTest(RSocketRequester requester) {
// this mono completes empty, no security context available :(
Mono<SecurityContext> context = ReactiveSecurityContextHolder.getContext();
return Flux.empty();
}
}
I found many resources how to setup authentication with RSocket, but they all rely on an additional authentication after the WebSocket connection is established, but I specifically want to reuse the web session and don't want to send additional tokens over the websocket.
Have you tried the following? I found it in the documentation: 2.2 Secure Your RSocket Methods (might have to scroll down a bit) https://spring.io/blog/2020/06/17/getting-started-with-rsocket-spring-security
#PreAuthorize("hasRole('USER')") // (1)
#MessageMapping("fire-and-forget")
public Mono<Void> fireAndForget(final Message request, #AuthenticationPrincipal UserDetails user) { // (2)
log.info("Received fire-and-forget request: {}", request);
log.info("Fire-And-Forget initiated by '{}' in the role '{}'", user.getUsername(), user.getAuthorities());
return Mono.empty();
}
You can get the user information using #AuthenticationPrincipal Mono<UserDetails> userDetails.
In case someone use JWT authentication as me you need to add #AuthenticationPrincipal Mono<Jwt> jwt to your method arguments.
But for this to work, you need to configure the RSocketMessageHandler bean, that resolvs the argument.
#Bean
public RSocketMessageHandler rSocketMessageHandler(RSocketStrategies strategies) {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.getArgumentResolverConfigurer()
.addCustomResolver(new AuthenticationPrincipalArgumentResolver());
handler.setRSocketStrategies(strategies);
return handler;
}
Important you have to use org.springframework.security.messaging.handler.invocation.reactive.AuthenticationPrincipalArgumentResolver() class as the resolver, and for that you need spring-security-messaging dependency.

Spring security - Get SESSION cookie value in AuthenticationSuccessHandler

I know that spring security creates a cookies names SESSION on successful authentication. Is it possible to get hold of that cookie value in AuthenticationSuccessHandler.
I have a following implementation inside which I need that SESSION cookie value. I looked as response headers of HttpServletResponse, but they have XSRF-TOKEN set-cookie headers,
#Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
// GET SESSION, COOKIE VALUE HERE
}
}
Can you please help.
The SESSION cookie is created by Spring Session's DefaultCookieSerializer, which is called every time a new Session is created, and not necessarily after successful authentication.
Spring Session's SessionRepositoryFilter wraps the HttpServletRequest in such a way that whenever you obtain an HttpSession from the request at any point in your application, you're actually getting a Spring Session object. However, this cookie is written to the response after your handler has been called, as you can see in SessionRepositoryFilter:
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession(); //the SESSION cookie is created if necessary
}
So if the session has just been created for this request...
The cookie won't be available in the HttpServletRequest because the cookie hasn't been sent yet (and so the browser couldn't have sent it)
The cookie won't be HttpServletResponse as a "Set-Cookie" header because it will be written after your application has handled the request.
However, you could get the cookie value:
String cookieValue = request.getSession().getId();
Note: The above code will force Spring Session to create a session backed Redis/Jdbc/etc that will be used later to generate the SESSION cookie.
I got it using the getSession().getId() method from request. My example is using the Webflux implementation with Kotlin but apparently works similar in HttpServletRequest implementation see https://javaee.github.io/javaee-spec/javadocs/javax/servlet/http/HttpServletRequest.html#getSession--
class AuthenticationSuccessHandler : ServerAuthenticationSuccessHandler {
private val location = URI.create("https://redirect.page")
private val redirectStrategy: ServerRedirectStrategy = DefaultServerRedirectStrategy()
override fun onAuthenticationSuccess(webFilterExchange: WebFilterExchange?, authentication: Authentication?): Mono<Void> {
val exchange = webFilterExchange!!.exchange
return exchange.session.flatMap {
it.id // 87b5639c-7404-48a1-b9da-3ca47691a962
this.redirectStrategy.sendRedirect(exchange, location)
}
}
}

Authentication in Spring MVC via REST

I've been looking for a way to authenticate a user via REST controller (URL params).
The closest thing to do so is the following:
#Controller
#RequestMapping(value="/api/user")
public class UserController extends BaseJSONController{
static Logger sLogger = Logger.getLogger(UserController.class);
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody String login(#RequestParam(value="username") String user, #RequestParam(value="password") String pass) throws JSONException {
Authentication userAuth = new UsernamePasswordAuthenticationToken(user, pass);
MyCellebriteAuthenticationProvider MCAP = new MyCellebriteAuthenticationProvider();
if (MCAP.authenticate(userAuth) == null){
response.put("isOk", false);
}
else{
SecurityContextHolder.getContext().setAuthentication(userAuth);
response.put("isOk", true);
response.put("token", "1234");
}
return response.toString();
}
}
However, this doesn't create a cookie.
Any idea or a better way to implement what I want to achieve?
Firstly, you should not do this manually:
SecurityContextHolder.getContext().setAuthentication(userAuth)
It is better to employ special filter responsible for authentication, setting security context and clearing it after request is handled. By default Spring Security uses thread locals to store security context so if you don't remove it after client invocation, another client can be automatically logged in as someone else. Remember that server threads are often reused for different request by different clients.
Secondly, I would recommend using basic or digest authentication for your RESTful web service. Both are supported by Spring Security. More in docs http://static.springsource.org/spring-security/site/docs/3.1.x/reference/basic.html
And finally, remember that RESTful web service should be stateless.
Also remember that Spring Security documentation is your friend. :-)

Spring Security: How to clear `remember me` cookie programmatically?

I'm using logout method in web-app like below, but if i check remember me logout doesn't work, because cookie isn't cleared. How to clear programmatically this cookie in my method (or how to make better logout method) ?
public void logout() {
AnonymousAuthenticationToken anonymous = new AnonymousAuthenticationToken("anonymous", "anonymous", new ArrayList(Arrays.asList(new GrantedAuthorityImpl("ROLE_ANONYMOUS"))));
SecurityContextHolder.getContext().setAuthentication(anonymous);
}
If you are using the standard Spring Security cookie name (which is SPRING_SECURITY_REMEMBER_ME_COOKIE), you can do this:
void cancelCookie(HttpServletRequest request, HttpServletResponse response)
{
String cookieName = "SPRING_SECURITY_REMEMBER_ME_COOKIE";
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
response.addCookie(cookie);
}
You'll have to change the cookieName value if you are using a custom cookie name.
The AbstractRememberMeServices class has an implementation of LogoutHandler.logout which cancels the cookie. Inject the LogoutHandler and call this method.

Resources