I fail to retrieve the Spring remember me cookie after the login request, but it works fine in the next request to a protected page. Could anyone please tell me how I can get hold of it right away?
I am setting the remember me cookie in the login request, but fail to retrive it after Spring redirects back to the original (protected) url.
Step by step:
Browser goes to example.com/protected
Spring redirects to login form page
Upon successful login, the SPRING_SECURITY_REMEMBER_ME_COOKIE is set in a very thin custom sub class of org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices
It looks like Spring redirects back to example.com/protected, whithout a roundtrip to the browser, and both login "servlet" and protected page is handled by the same thread in Tomcat 6.
Our subclass of org.springframework.security.web.access.expression.WebSecurityExpressionRoot has methods that is invoked from a <intercept-url pattern="..." access="method()" />
In our method() request.getCookies() does not give the remember me cookie on the first request, but on all requests after that.
Our app has some problems because the cookie is missing...
My theory so far is that I don't understand SavedRequest properly.
Condensed config here:
<http auto-config="false" use-expressions="true" authentication-manager-ref="myAuthenticationManager" path-type="regex">
<form-login authentication-success-handler-ref="myAuthenticationSuccessHandler" login-page="..." login-processing-url="..." authentication-failure-url="..." username-parameter="username" password-parameter="password" />
<custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
<expression-handler ref="myWebSecurityExpressionHandler" />
<custom-filter ref="myCustomeFilter1" before="FORM_LOGIN_FILTER"/>
<custom-filter ref="myCustomeFilter2" position="BASIC_AUTH_FILTER"/>
<custom-filter ref="mySecurityClientTokenAuthenticationFilter" after="LOGOUT_FILTER" />
<access-denied-handler ref="myAccessDeniedHandler"/>
<intercept-url pattern="xxx"
access="method()"/>
<intercept-url pattern="yyy"
access="method()"/>
<remember-me services-ref="rememberMeServices" key="my_remember"/>
</http>
I tried adding the following, with the only result that the user does not get redirected to the original page.
<http ...
<request-cache ref="nullRequestCache"/>
</http>
<bean:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
When using request.getCookie() in autoLogin() method of RememberMeService, the request passed in is SavedRequestAwareWrapper which encapsulates original request and saved request and overrides the getCookies method.
#Override
public Cookie[] getCookies() {
List<Cookie> cookies = savedRequest.getCookies();
return cookies.toArray(new Cookie[cookies.size()]);
}
Therefore, when you want to get cookie from request, you actually get cookie from the savedRequest. However, the cookie may exist in the original request.
You probably should get the original request for getting cookies you want. For example:
public class ApplicationRememberMeServiceImpl implements RememberMeService, LogoutHandler {
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequestWrapper savedRequestWrapper = (HttpServletRequestWrapper) ((HttpServletRequestWrapper) request).getRequest();
HttpServletRequest httpServletRequest = (HttpServletRequest) savedRequestWrapper.getRequest();
Cookie cookie = WebUtils.getCookie(httpServletRequest, cookieName);
// logic continues...
}
}
Update 03/05/2015
Because spring security will wrap the original HttpServletRequest multiple times, it is more safe to extracting the original request in the way below:
public class ApplicationRememberMeServiceImpl implements RememberMeService, LogoutHandler {
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest httpServletRequest = request;
// Get the original request from multiple wrapped HttpServletRequest
if(httpServletRequest instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) httpServletRequest;
while(httpServletRequestWrapper.getRequest() instanceof HttpServletRequestWrapper) {
httpServletRequestWrapper = (HttpServletRequestWrapper) httpServletRequestWrapper.getRequest();
}
httpServletRequest = (HttpServletRequest) httpServletRequestWrapper.getRequest();
}
Cookie cookie = WebUtils.getCookie(httpServletRequest, cookieName);
// logic continues...
}
}
Related
I am trying to do authentication for my web application using widely used OpenId Connect flow.
I understand that the user who wants to authenticate on example1.com will first be redirected to the authentication server (or OP : "OpenId Provider"). The user authenticates on that server which then redirects him back to the original example1.com site with a signed JWT token.
Technical challenges:
I am suing spring security as part of my web application. I took a custom filter to redirect the user to authentication server and then it redirects back to my example1.com with valid JWT (Json Web Token).
My Custom Filter:
public class MyCustomFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
//reading the AWT access token from reqest beahder
String token = request.getHeader("Authorization");
if(token == null){
// redirecting user to authentication server
String accessCodeUrl = "authentication.com?query string"
response.sendRedirect(accessCodeUrl);
} else{
response.addHeader("Authorization", token);
}
//Now how can i inject the above received token to spring security
}
}
My spring xml config:
<security:http auto-config="false" entry-point-ref="entryPoint" disable-url-rewriting="true">
<!-- set disabled to true to remove Cross Site Request Forger support -->
<security:csrf disabled="true" />
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
<security:custom-filter ref="${MyCustomFilter }" position="PRE_AUTH_FILTER" />
<security:access-denied-handler error-page="${security.accessDeniedPage}"/>
<security:headers>
<security:frame-options policy="SAMEORIGIN"/>
</security:headers>
</security:http>
My custom filter is extending AbstractAuthenticationProcessingFilter. is it good fit for my requirement or should i consider extending any other filter's like BasicAuthenticationFilter or OncePerRequestFilter or GenericFilterBean.
I am confused here as to take 1 or 2 filters. because first time i am redirecting to authorization server, but from second time i should use the JWT token provided by authentication server.
Since i am going with stateless approach. I need to exchange JWT token every time between example.com and browser. I am using ExtJs as front end, what is the best practice to exchange JWT token between browser and example.com.
As per my understanding i should send JWT token every time from browser along with every request. Does that mean should i manually inject every time JWT token as part of Ajax request or Do we have some approach to append to all the outgoing Ajax requests.
In a Spring MVC application I am trying to implement a custom logout success handler. This handler should access a session attribute and make some queries and logging bases on its value.
Relevant parts of the implementation:
Security configuration:
<http ...>
<logout success-handler-ref="logoutSuccessHandler"/>
</http>
<beans:bean id="logoutSuccessHandler" class="some.package.LogoutSuccessHandler">
<beans:constructor-arg value="/login" />
</beans:bean>
The handler itself:
public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
#Autowired
private SomeService someService;
public LogoutSuccessHandler(String defaultTargetUrl) {
this.setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String sessionAttribute = request.getSession().getAttribute("someAttribute").toString();
someService.doSomething(sessionAttribute);
super.onLogoutSuccess(request, response, authentication);
}
}
I am adding some attributes to the session when a user logs in. They are visible during different controller requests. The problem is, when I try to access session attributes during logout, they are all gone. Does that mean that by that time the logout has already taken place and session information is wiped out? I can see that SessionId is the same as before though.
So, my question is: what happens to the session attributes and is there a way to access them in LogoutSuccessHandler?
<logout success-handler-ref="logoutSuccessHandler" invalidate-session="false"/>
the default value of invalidate-session is true, so you will get a new session in your handler.
When you set this value to false, then you can get the old session, and don't forget to invalidate session after you finished your business.
I'm using Spring Boot and Spring Security. I have 2 apps. They works in couple. And when I want to logout from one, it should also logout from another. I use default logout flow. On application A I click logout link:
<logout invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/app-b-logout" />
#RequestMapping("/app-b-logout")
public RedirectView appB_Logout() {
return new RedirectView("http://appB/logout");
}
On application B I have following settings:
.logout().permitAll().logoutUrl("/logout")
And logout success handler:
public class MyLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
implements LogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println(authentication == null); // MAGIC HERE
}
}
And in line System.out.println(authentication == null); I have different results. If I redirect from application A, authentication is null, when I click LOGOUT button on app B (using post form), I have authentication object.
But in both cases user is logged out in result. But I need to do some stuff with authentication object. How to make redirect on app A to make a POST request or just how to get this authentication when I redirect from app A?
I have a web project with Spring Security and I have tried to save a cookie in the method that process the authentication success. However, when I look to the browser's cookies only appears the JSESSIONID one, and the same happens when I look to request.getCookies() at the servlet that Spring redirects to.
I have tried to save the cookie in one of the application's servlets and the cookie is saved correctly, so maybe Spring Security cleans the response. Do you have any idea?
One workaround would be to save it in Session, and then get it and save the cookie on the servlet to which the login redirects. Another one would be saving the cookie with javascript like this. But I don't like these solutions. Thanks in advance
Here is the relevant code:
public class RoleBasedAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler implements
AuthenticationSuccessHandler {
...
// save a cookie with the selected language
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap.containsKey("language")) {
saveCookie("language", parameterMap.get("language")[0], response);
}
}
public static void saveCookie(String cookieName, String value, HttpServletResponse response) {
Cookie cookie = new Cookie(cookieName, value);
//maxAge is one month: 30*24*60*60
cookie.setMaxAge(2592000);
cookie.setDomain("projectName");
cookie.setPath("/");
response.addCookie(cookie);
}
}
<security:http auto-config="false" ...>
<security:form-login login-page="/login.do" authentication-success-handler-ref="redirectRoleStrategy" .../>
...
</security:http>
<bean id="redirectRoleStrategy" class="com.companyName.security.RoleBasedAuthenticationSuccessHandler">
<beans:property name="roleUrlMap">
<beans:map>
<beans:entry key="ROLE_ADMIN" value="/privat/application.do"/>
...
</beans:map>
</beans:property>
</bean>
Are you setting the cookie before or after calling super in the RoleBasedAuthenticationSuccessHandler?
super.onAuthenticationSuccess(request, response, authentication);
You must set the cookie before your call to the super, as the logic in the superclass will send a redirect and therefore prevent you from updating content of the HttpServletResponse.
Try to call some harcoded value outside the if clause, just to see if it works:
saveCookie("language", "en", response);
Also as a test try to not set cookie domain and path initially:
Cookie cookie = new Cookie(cookieName, value);
//maxAge is one month: 30*24*60*60
cookie.setMaxAge(2592000);
//cookie.setDomain("projectName");
//cookie.setPath("/");
response.addCookie(cookie);
It should be possible to set a cookie from the authentication successful handler, this should normally work.
Problem:
We have a Spring MVC-based RESTful API which contains sensitive information. The API should be secured, however sending the user's credentials (user/pass combo) with each request is not desirable. Per REST guidelines (and internal business requirements), the server must remain stateless. The API will be consumed by another server in a mashup-style approach.
Requirements:
Client makes a request to .../authenticate (unprotected URL) with credentials; server returns a secure token which contains enough information for the server to validate future requests and remain stateless. This would likely consist of the same information as Spring Security's Remember-Me Token.
Client makes subsequent requests to various (protected) URLs, appending the previously obtained token as a query parameter (or, less desirably, an HTTP request header).
Client cannot be expected to store cookies.
Since we use Spring already, the solution should make use of Spring Security.
We've been banging our heads against the wall trying to make this work, so hopefully someone out there has already solved this problem.
Given the above scenario, how might you solve this particular need?
We managed to get this working exactly as described in the OP, and hopefully someone else can make use of the solution. Here's what we did:
Set up the security context like so:
<security:http realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="CustomAuthenticationEntryPoint">
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/authenticate" access="permitAll"/>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
<bean id="CustomAuthenticationEntryPoint"
class="com.demo.api.support.spring.CustomAuthenticationEntryPoint" />
<bean id="authenticationTokenProcessingFilter"
class="com.demo.api.support.spring.AuthenticationTokenProcessingFilter" >
<constructor-arg ref="authenticationManager" />
</bean>
As you can see, we've created a custom AuthenticationEntryPoint, which basically just returns a 401 Unauthorized if the request wasn't authenticated in the filter chain by our AuthenticationTokenProcessingFilter.
CustomAuthenticationEntryPoint:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
}
}
AuthenticationTokenProcessingFilter:
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
#Autowired UserService userService;
#Autowired TokenUtils tokenUtils;
AuthenticationManager authManager;
public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
this.authManager = authManager;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
#SuppressWarnings("unchecked")
Map<String, String[]> parms = request.getParameterMap();
if(parms.containsKey("token")) {
String token = parms.get("token")[0]; // grab the first "token" parameter
// validate the token
if (tokenUtils.validate(token)) {
// determine the user based on the (already validated) token
UserDetails userDetails = tokenUtils.getUserFromToken(token);
// build an Authentication object with the user's info
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
// set the authentication into the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(authentication));
}
}
// continue thru the filter chain
chain.doFilter(request, response);
}
}
Obviously, TokenUtils contains some privy (and very case-specific) code and can't be readily shared. Here's its interface:
public interface TokenUtils {
String getToken(UserDetails userDetails);
String getToken(UserDetails userDetails, Long expiration);
boolean validate(String token);
UserDetails getUserFromToken(String token);
}
That ought to get you off to a good start.
You might consider Digest Access Authentication. Essentially the protocol is as follows:
Request is made from client
Server responds with a unique nonce string
Client supplies a username and password (and some other values) md5 hashed with the nonce; this hash is known as HA1
Server is then able to verify client's identity and serve up the requested materials
Communication with the nonce can continue until the server supplies a new nonce (a counter is used to eliminate replay attacks)
All of this communication is made through headers, which, as jmort253 points out, is generally more secure than communicating sensitive material in the url parameters.
Digest Access Authentication is supported by Spring Security. Notice that, although the docs say that you must have access to your client's plain-text password, you can successfully authenticate if you have the HA1 hash for your client.
Regarding tokens carrying information, JSON Web Tokens (http://jwt.io) is a brilliant technology. The main concept is to embed information elements (claims) into the token, and then signing the whole token so that the validating end can verify that the claims are indeed trustworthy.
I use this Java implementation: https://bitbucket.org/b_c/jose4j/wiki/Home
There is also a Spring module (spring-security-jwt), but I haven't looked into what it supports.
Why don't you start using OAuth with JSON WebTokens
http://projects.spring.io/spring-security-oauth/
OAuth2 is an standardized authorization protocol/framework. As per Official OAuth2 Specification:
You can find more info here