spring-security block websocket (sockjs) - spring

In one of my projects I configured both rest services and websockets, both go through spring security filter that check for JWT. For websockets on the client side, application uses sockjs & stomp (on Angular2) and Spring websockets on the server side (Tomcat 8). When I open connection with Spring security enabled then I get below error two seconds after it gets opened. However when I open connection without spring security enabled connection does not get dropped.
angular2 connect()/subscribe()/send() - all go with JWT token
public connect() : void {
let sockjs = new SockJS('/rest/add?jwt=' + this.authService.getToken());
let headers : any = this.authService.getAuthHeader();
this.stompClient = Stomp.over(sockjs);
this.stompClient.connect(this.token, (frame) => {
this.log.d("frame", "My Frame: " + frame);
this.log.d("connected()", "connected to /add");
this.stompClient.subscribe('/topic/addMessage', this.authService.getAuthHeader(), (stompResponse) => {
// this.stompSubject.next(JSON.parse(stompResponse.body));
this.log.d("result of WS call: ", JSON.parse(stompResponse.body).message);
}, (error) => {
this.log.d(error);
});
});
}
public send(payload: string) {
this.stompClient.send("/app/add", this.token, JSON.stringify({'message': payload}));
}
JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationFilter() {
super("/rest/**");
}
#Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return true;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String token = null;
String param = request.getParameter("jwt");
if(param == null) {
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
throw new JwtAuthenticationException("No JWT token found in request headers");
}
token = header.substring(7);
} else {
token = param;
}
JwtAuthenticationToken authRequest = new JwtAuthenticationToken(token);
return getAuthenticationManager().authenticate(authRequest);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
// As this authentication is in HTTP header, after success we need to continue the request normally
// and return the response as if the resource was not secured at all
chain.doFilter(request, response);
}
}
JwtAuthenticationProvider.java
#Service
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
#Autowired
private SecurityService securityService;
#Override
public boolean supports(Class<?> authentication) {
return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
}
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
#Override
#Transactional(readOnly=true)
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
String token = jwtAuthenticationToken.getToken();
User user = securityService.parseToken(token);
if (user == null) {
throw new JwtAuthenticationException("JWT token is not valid");
}
return new AuthenticatedUser(user);
}
}
JwtAuthenticationSuccessHandler.java
#Service
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// We do not need to do anything extra on REST authentication success, because there is no page to redirect to
}
}
RestAuthenticationEntryPoint.java
#Service
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
// This is invoked when user tries to access a secured REST resource without supplying any credentials
// We should just send a 401 Unauthorized response because there is no 'login page' to redirect to
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Weboscket configuration:
<websocket:message-broker
application-destination-prefix="/app">
<websocket:stomp-endpoint path="/add">
<websocket:sockjs />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic, /queue" />
</websocket:message-broker>
and my spring security
<context:component-scan base-package="com.myapp.ws.security"/>
<sec:global-method-security pre-post-annotations="enabled" />
<!-- everyone can try to login -->
<sec:http pattern="/rest/login/" security="none" />
<!--<sec:http pattern="/rest/add/**" security="none" />-->
<!-- only users with valid JWT can access protected resources -->
<sec:http pattern="/rest/**" entry-point-ref="restAuthenticationEntryPoint" create-session="stateless">
<!-- JWT is used to disabled-->
<sec:csrf disabled="true" />
<!-- don't redirect to UI login form -->
<sec:custom-filter before="FORM_LOGIN_FILTER" ref="jwtAuthenticationFilter" />
</sec:http>
<bean id="jwtAuthenticationFilter" class="com.myapp.ws.security.JwtAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler" ref="jwtAuthenticationSuccessHandler" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="jwtAuthenticationProvider" />
</sec:authentication-manager>

Your problem doesn't related to security. You just pass wrong arguments in Stomp connect and subscribe functions.
The connect() method also accepts two other variants if you need to
pass additional headers:
client.connect(headers, connectCallback);
client.connect(headers, connectCallback, errorCallback);
where header is a map and connectCallback and errorCallback are
functions.
this.stompClient.connect(this.token, (frame) => {
should be
this.stompClient.connect({}, (frame) => {
and
You can use the subscribe() method to subscribe to a destination. The
method takes 2 mandatory arguments: destination, a String
corresponding to the destination and callback, a function with one
message argument and an optional argument headers, a JavaScript object
for additional headers.
var subscription = client.subscribe("/queue/test", callback);
this.stompClient.subscribe('/topic/addMessage', this.authService.getAuthHeader(), (stompResponse) => {
should be
this.stompClient.subscribe('/topic/addMessage', (stompResponse) => {
Documentation http://jmesnil.net/stomp-websocket/doc/

#user1516873 finally I got it working:
passing correct parameters to STOMP fixed one problem
adding {transports: ["websocket"]} was not necessary (it works without it)
Problem was that I was using angular-cli server on port 4200 with proxy file like this:
{
"/rest": {
"target": "http://localhost:8080",
"secure": false
}
}
but should have been like this:
{
"/rest": {
"target": "http://localhost:8080",
"secure": false,
"ws": true,
"logLevel": "debug"
}
}
so through all the combinations of configuration I was always checking through 4200 proxy server and very rarely through native 8080 directly. I just didn't know that angular-cli proxy does not support when spring security is applied. I will accept your answer as you helped a lot!

Related

Keycloak spring adapter - check that the authToken is active with every http request

Problem I want to solve:
For every call made to the service I want to check that the token is active, if it isn't active I want to redirect the user to the login page.
Current setup: Grails 3.2.9 , Keycloak 3.4.3
Ideas so far:
This article looked promising: https://www.linkedin.com/pulse/json-web-token-jwt-spring-security-real-world-example-boris-trivic
In my security config I added a token filter
#Bean
public TokenAuthenticationFilter authenticationTokenFilter() throws Exception {
return new TokenAuthenticationFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure http
http
.addFilterBefore(authenticationTokenFilter(), BasicAuthenticationFilter.class)
.logout()
.logoutSuccessUrl("/sso/login") // Override Keycloak's default '/'
.and()
.authorizeRequests()
.antMatchers("/assets/*").permitAll()
.anyRequest().hasAnyAuthority("ROLE_ADMIN")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
My TokenAuthenticationFilter just prints out the request headers at the moment :
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private String getToken( HttpServletRequest request ) {
Enumeration headerEnumeration = request.getHeaderNames();
while (headerEnumeration.hasMoreElements()) {
println "${ headerEnumeration.nextElement()}"
}
return null;
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String authToken = getToken( request );
}
}
Which returns:
host
user-agent
accept
accept-language
accept-encoding
cookie
connection
upgrade-insecure-requests
cache-control
The code/logic I want to implement in the filter is something like:
KeycloakAuthenticationToken token = SecurityContextHolder.context?.authentication
RefreshableKeycloakSecurityContext context = token.getCredentials()
if(!context.isActive()){
// send the user to the login page
}
However I'm lost as to how to get there.
Any help greatly appreciated
As far as I understand, your question is about "how to check the token is active?" and not "how to redirect the user to login page?".
As I see you added the tag "spring-boot" and "keycloak" maybe you could use "Keycloak Spring Boot Adapter". Assuming you use the version 3.4 of Keycloak (v4.0 still in beta version), you can found some documentation here.
If you can't (or don't want to) use Spring Boot Adapter, here is the part of the KeycloakSecurityContextRequestFilter source code that could be interesting for your case:
KeycloakSecurityContext keycloakSecurityContext = getKeycloakPrincipal();
if (keycloakSecurityContext instanceof RefreshableKeycloakSecurityContext) {
RefreshableKeycloakSecurityContext refreshableSecurityContext = (RefreshableKeycloakSecurityContext) keycloakSecurityContext;
if (refreshableSecurityContext.isActive()) {
...
} else {
...
}
}
and here is the (Java) source code of the getKeycloakPrincipal method:
private KeycloakSecurityContext getKeycloakPrincipal() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
if (principal instanceof KeycloakPrincipal) {
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
}
}
return null;
}
And if you want to understand how the Authentication is set in the SecurityContextHolder, please read this piece of (Java) code from KeycloakAuthenticationProcessingFilter:
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (authResult instanceof KeycloakAuthenticationToken && ((KeycloakAuthenticationToken) authResult).isInteractive()) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
...
SecurityContextHolder.getContext().setAuthentication(authResult);
...
try {
chain.doFilter(request, response);
} finally {
SecurityContextHolder.clearContext();
}
}
As an alternative you could also check this github repository of dynamind:
https://github.com/dynamind/grails3-spring-security-keycloak-minimal
Hoping that can help.
Best regards,
Jocker.

SSO with Spring security

I have an application, where user is pre-authorized by SSO and lands to my page, now I need to make a call to another rest api to get some data, which is running on another server, but it will be use the same authentication. So I just wanted to know, how I can provide the authentication process? Do I need to set the cookie what I am getting from the incoming request.
When the request lands on your page it should have a token or key, in the http AUTHORIZATION header, this should be used with a filter
public class AuthFilter extends OncePerRequestFilter {
private String failureUrl;
private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
// check your SSO token here
chain.doFilter(request, response);
} catch (OnlineDriverEnquiryException ode) {
failureHandler.setDefaultFailureUrl(failureUrl);
failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Captcha invalid!"));
}
}
public String getFailureUrl() {
return failureUrl;
}
public void setFailureUrl(String failureUrl) {
this.failureUrl = failureUrl;
}
}
Also read this post on how to set up the auto config. Spring security without form login

Session timeout leads to Access Denied in Spring MVC when CSRF integration with Spring Security

I have Integrated CSRF token with Spring Security in my Spring MVC Project. Everything work properly with CSRF token, token will be send from client side to server side.
I have changed my logout process to make it POST method to send CSRF token and its works fine.
I have face problem when session timeout is occurred, it needs to be redirected to spring default logout URL but it gives me Access Denied on that URL.
How to override this behavior.
I have include below line in Security config file
<http>
//Other config parameters
<csrf/>
</http>
Please let me know if anyone needs more information.
The question is a bit old, but answers are always useful.
First, this is a known issue with session-backed CSRF tokens, as described in the docs: CSRF Caveats - Timeouts.
To solve it, use some Javascript to detect imminent timeouts, use a session-independent CSRF token repository or create a custom AccessDeniedHandler route. I chose the latter:
Config XML:
<http>
<!-- ... -->
<access-denied-handler ref="myAccessDeniedHandler"/>
</http>
<bean id="myAccessDeniedHandler" class="package.MyAccessDeniedHandler">
<!-- <constructor-arg ref="myInvalidSessionStrategy" /> -->
</bean>
MyAccessDeniedHandler:
public class MyAccessDeniedHandler implements AccessDeniedHandler {
/* ... */
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception)
throws IOException, ServletException {
if (exception instanceof MissingCsrfTokenException) {
/* Handle as a session timeout (redirect, etc).
Even better if you inject the InvalidSessionStrategy
used by your SessionManagementFilter, like this:
invalidSessionStrategy.onInvalidSessionDetected(request, response);
*/
} else {
/* Redirect to a error page, send HTTP 403, etc. */
}
}
}
Alternatively, you can define the custom handler as a DelegatingAccessDeniedHandler:
<bean id="myAccessDeniedHandler" class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">
<constructor-arg name="handlers">
<map>
<entry key="org.springframework.security.web.csrf.MissingCsrfTokenException">
<bean class="org.springframework.security.web.session.InvalidSessionAccessDeniedHandler">
<constructor-arg name="invalidSessionStrategy" ref="myInvalidSessionStrategy" />
</bean>
</entry>
</map>
</constructor-arg>
<constructor-arg name="defaultHandler">
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/my_error_page"/>
</bean>
</constructor-arg>
</bean>
The answer provided by mdrg is right on, and I also implemented a custom AccessDeniedHandler which I submit for your consideration:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
/**
* Intended to fix the CSRF Timeout Caveat
* (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-timeouts).
* When the session expires and a request requiring CSRF is received (POST), the
* missing token exception is handled by caching the current request and
* redirecting the user to the login page after which their original request will
* complete. The intended result is that no loss of data due to the timeout will
* occur.
*/
public class MissingCsrfTokenAccessDeniedHandler extends AccessDeniedHandlerImpl {
private RequestCache requestCache = new HttpSessionRequestCache();
private String loginPage = "/login";
#Override
public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException exception) throws IOException, ServletException {
if (exception instanceof MissingCsrfTokenException && isSessionInvalid(req)) {
requestCache.saveRequest(req, res);
res.sendRedirect(req.getContextPath() + loginPage);
}
super.handle(req, res, exception);
}
private boolean isSessionInvalid(HttpServletRequest req) {
try {
HttpSession session = req.getSession(false);
return session == null || !req.isRequestedSessionIdValid();
}
catch (IllegalStateException ex) {
return true;
}
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}
Wired up via java config:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
...
}
public AccessDeniedHandler getAccessDeniedHandler() {
return new MissingCsrfTokenAccessDeniedHandler();
}
}

How to handle exceptions properly in custom Spring security 3.0 authentication?

I'm developing a REST service based in tokens. When an user goes to ../rest/authenticate with the user and password via curl, gets a valid token in order to use the whole API.
My problem appears when the user forgets to insert the username, the password or the token in the other methods because i've not managed to handle the Authentication exceptions as I want.
I cand handle the exceptions but tomcat gets the response and inserts some html that I don't expect.
This is the typical response of tomcat.
Is it possible to receive a response like 200 OK which don't have this html code?
At the momment, this is my config:
AuthenticationProcessingFilter
Decides if the url is secured or not. If has to be secured, calls the authentication manager in order to validate it. If receives an authentication exceptions calls the AuthenticationEntryPoint
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
private final Collection<String> nonTokenAuthUrls = Lists.newArrayList("/rest","/rest/authenticate");
TokenAuthenticationManager tokenAuthenticationManager;
RestAuthenticationEntryPoint restAuthenticationEntryPoint;
public AuthenticationTokenProcessingFilter(TokenAuthenticationManager tokenAuthenticationManager, RestAuthenticationEntryPoint restAuthenticationEntryPoint) {
this.tokenAuthenticationManager = tokenAuthenticationManager;
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
try{
if(!nonTokenAuthUrls.contains(httpRequest.getRequestURI())){ //Auth by token
String hash = httpRequest.getHeader("token");
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(hash, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
SecurityContextHolder.getContext().setAuthentication(tokenAuthenticationManager.authenticate(authentication));
}
response.reset();
chain.doFilter(request, response);
}catch(AuthenticationException authenticationException){
SecurityContextHolder.clearContext();
restAuthenticationEntryPoint.commence(httpRequest, httpResponse, authenticationException);
}
}
AuthenticationManager
public class TokenAuthenticationManager implements AuthenticationManager{
#Autowired
UserService userService;
#Autowired
TokenService tokenService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Object hash = authentication.getPrincipal();
if(hash == null)
throw new BadCredentialsException("Token is required");
User user = tokenService.getUserFromTokenHash((String)hash);
if(user == null)
throw new BadCredentialsException("Non-existent token");
if(!tokenService.validate((String)hash))
throw new BadCredentialsException("Expired Token");
org.springframework.security.core.userdetails.User userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
return new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
}
AuthenticationEntryPoint
This class works OK. The code received is 401 unauthorized but the message is in the tomcat html
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
response.setContentType("application/json");
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage() );
response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");
}
}
The RestAccessDeniedHanler is not called either. It's difficult becasue there are a lot of classes that have to be implemented.
I reviewed some post in stackoverflow and other websites and my approach consist on catching the exceptions in the AuthenticationProcessingFilter and call the AuthenticationEntryPoint manually. I decided to do that becasue I've tried to configure this in the applicationContext-security.xml with no success.
appliacionContext-security.xml
<b:bean id="restAuthenticationEntryPoint" class="...web.security.RestAuthenticationEntryPoint" />
<b:bean id="tokenAuthenticationManager" class="...dp.web.security.TokenAuthenticationManager"/>
<b:bean id="AuthenticationTokenProcessingFilter" class="...web.security.AuthenticationTokenProcessingFilter">
<b:constructor-arg type="...dp.web.security.TokenAuthenticationManager" ref="tokenAuthenticationManager"></b:constructor-arg>
<b:constructor-arg type="...dp.web.security.RestAuthenticationEntryPoint" ref="restAuthenticationEntryPoint"></b:constructor-arg>
</b:bean>
<b:bean id="accessDeniedHandler" class="...dp.web.security.RestAccessDeniedHandler">
</b:bean>
<http realm="Protected REST API" pattern="/rest/**" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="restAuthenticationEntryPoint">
<custom-filter ref="AuthenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<access-denied-handler ref="accessDeniedHandler"/>
</http>
how can I send a clean response with the error code and a message?
You can use error-pages in your web.xml to intercept Tomcat's error page. For example,
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
Now you use RequestMapping to map /404 to a page that returns your JSON response without any HTML:
#RequestMapping(value = "/404", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
#ResponseBody
public ResponseEntity<ResponseStatus> handle404() {
HttpStatus status = null;
ResponseStatus responseStatus = new ResponseStatus("404", "Wrong path to resource.");
status = HttpStatus.NOT_FOUND;
ResponseEntity<ResponseStatus> response = new ResponseEntity<ResponseStatus>(responseStatus, status);
return response;
}
Which will simply return a JSON object called Response Status that contains an error code and error message as fields.

Spring Security login with a rest web service

I have a SpringMVC web application that needs to authenticate to a RESTful web service using Spring Security.And i need to access this same application through a rest client.
Here is What I need to implement
The accept header is application/json(For a java rest client )
After a successful login, It will be sent a token(Or sessionId) to rest client in the format of json
After a login failure,It will be sent error message in the format of json.
For a web request
After a successful login,It will be redirecting to a success jsp page.
After a login failure,It will be sent error message to the same loin page.
How can i do this with spring mvc and spring security?.I have very less time to do this,any one please give me an example with spring-security.xml.
Thanks
my recommendation is as below. you can use standard web security to call RESTFul service, first authenticate with user and password and get cookies, if using java based server, send this as cookie to server on subsequent rest calls. I have written is Spring Java code which can get session cookies for you.
There is no need for separate json service to get token.
public class RestAuthClient {
String baseUrl = "http://localhost:8888/ecom";
public String authenticateGetCookie(String user, String password){
HttpMessageConverter<MultiValueMap<String, ?>> formHttpMessageConverter = new FormHttpMessageConverter();
HttpMessageConverter<String> stringHttpMessageConverternew = new StringHttpMessageConverter();
List<HttpMessageConverter<?>> messageConverters = new LinkedList<HttpMessageConverter<?>>();
messageConverters.add(formHttpMessageConverter);
messageConverters.add(stringHttpMessageConverternew);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("j_username", user);
map.add("j_password", password);
String authURL = baseUrl+"/j_spring_security_check";
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<MultiValueMap<String, String>>(map,
requestHeaders);
ResponseEntity<String> result = restTemplate.exchange(authURL, HttpMethod.POST, entity, String.class);
HttpHeaders respHeaders = result.getHeaders();
System.out.println(respHeaders.toString());
System.out.println(result.getStatusCode());
String cookies = respHeaders.getFirst("Set-Cookie");
return cookies;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
}
Consider implementing your custom AuthenticationSuccessHandler and AuthenticationFailureHandler as described below.
You might also need to implement some simple controllers which you will be redirecting to from AuthenticationHandlers. There's a good explanation of how to implement REST auth in Spring. So I beleive combining these two answers will give you a solution.
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// get accept headers from request
// Redirect successfully logged in user to another url depending on the accept headers)
// put session id in response if needed
((WebAuthenticationDetails)SecurityContextHolder.getContext().getAuthentication().getDetails()).getSessionId();
String targetUrl = ""; //TODO insert here
response.sendRedirect(targetUrl);
}
}
public class AuthenticationFailureHandlerImpl extends SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// get accept headers from request
// set failure url
// Do redirecting job
setDefaultFailureUrl(FAILURE_URL);
super.onAuthenticationFailure(request, response, exception);
}
}
In your security.xml
<http entry-point-ref="loginUrlAuthenticationEntryPoint" access-denied-page="/WEB-INF/views/errors/error403.jsp" access-decision-manager-ref="accessDecisionManager">
...
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"/>
...
</http>
<!-- Login filter and entry point -->
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/signin" /></beans:bean>
<beans:bean id="loginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="filterProcessesUrl" value="/j_spring_security_check"/>
<beans:property name="authenticationSuccessHandler" ref="authSuccessHandler"/>
<beans:property name="authenticationFailureHandler" ref="authFailureHandler"/></beans:bean>
<!-- Login filter and entry point -->
<beans:bean id="authSuccessHandler" class="com.example.security.AuthenticationSuccessHandlerImpl"/>
<beans:bean id="authFailureHandler" class="com.example.security.AuthenticationFailureHandlerImpl"/>
</beans:beans>

Resources