I have created CAS server and the CAS Client application. Basically the authentication of a service app has been done using service ticket and proxy ticket validation. In this case, initially I validate a particular user by validation of the service ticket and then authorize a user for a particular app by generating a proxy ticket using the service ticket. In this case I am able to successfully validate the service and proxy tickets in the service application. The problem that I am facing is that once I validated the user I can retrieve the user information via Assertion object using the following code segment.
Assertion assertion = serviceTicketValidator.validate(serviceTicket);
Here the serviceTicektValidator object is a class that I have written by implementing the Cas20ServiceTicketValidator valdation functions. Here the assertion object is set to the SecurityContextHolder as follows.
CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, serviceTicket);
SecurityContextHolder.getContext().setAuthentication(token);
Here this way, I am setting the authentication object. But the fact is once the request is changed or consider a new request, in the SecurityContextPersistenceFilter filter class in the following code segment the SecurityContext object is being cleared. So once a new request is fired the already stored authentication detail is vanished from the SecurityContextHolder object. So once every request the authentication details has to be set, because a particular service or proxy ticket can only be validated once. Here the approach taken by me is keeping the Assertion object and ticket generated in the Session and then at the beginning of this class (or actually I am doing all these in a filter) I check whether these values are in the session and again at each new request the CasAssertionAuthenticationToken is set to the SecurityContextHolder.
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
} finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything else.
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
The application is working the expected way, but I want to know whether there is a better way to implement this by using advanced techniques in Spring Filters or any other option. I also considered local storage of tickets and authentication information, but I want to do this within Spring Application even without using this session storage mechanism. I want to know whether this is possible or not?
Related
We are implementing a microfrontend, microservices architecture.
App1 is the microfrontend app - ui built on React, backend built on spring boot. It handles the authentication and provides the token to it's child app. The token is generated using Jwts as follows:
Jwts.build().setClaims(claims).setSubject(username).setExpiration(expirationDate)...
App2 is a child app of the microfrontend setup. It's ui is built on React, backend built on spring boot. App1 attaches App2 via react-iframe while passing the token as follows:
<Iframe url={`${urlOfApp2}`?token={jwtToken}} ... />
App2 on useEffect checks if window.location.search has the token field and use this to set the Authenticcation in its security context. This is done by calling endpoint /user in App2. The App2 backend will then call an endpoint /validate from App1 to check if the token is valid. If it is valid, App2 parses the token and creates an Authentication object and saves it to its context as follows:
final Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
This will create the JSESSIONID. So everytime an endpoint from App2 is requested, for example /someendpoint, it will check if the request has the required authority as set in the code above. The security config is as follows:
http...
.antMatchers("/user").permitAll()
.anyRequest().hasAuthority("SOME_AUTHORITY_PARSED_FROM_THE_TOKEN")...
This works as the /user is called once to check if token is valid and a session on App2 is initialized. So for succeeding requests, it will check if it has the proper authority.
The problem is, the session on App2 has different expiration compared to that set on the token. How can we sync the expiration on the session on App2 with the token provided by App1?
Several things here;
You are exposing the token in cleartext (the iframe url). Even when HTTPS/TLS is used this URL itself is always cleartext and can be seen by anyone on the network at either end, or a middle party along the physical devices that relays network packets from client to server, anyone can see the token who are joined to the same wifi network as an example but the largest concern with exposing sensitive information in the URL is these URLs are logged everywhere, on the same device in unprotected files that other software can access, on all the devices on the network paths along the way (maybe even routed through countries you're not thinking can read this traffic), and on the server the same logs are created and often not protected either and these logs are almost always also sent of to a logging service like Datadog, SUMO, (hundreds others) who also see this sensitive token in the URL from these logs.
Bottom line, never, ever, send anything sensitive in a URL.
Which JWT scheme are you implementing for the client?
a) Is there a private key generated by the client and the public key sent to the server to form a JWT with encryption? Or;
b) Is it using a HMAC which is essentially a signature accompanying the data to provide an integrity check, which only ensures data was not modified, this type of JWT has no encryption and therefore the JWT is entirely 'readable' by anyone by design
c) There is no actual authentication happening on the client at all, the client is provided the JWT to avoid having to do it.
If (a) then the client generated a key/pair and it needs to send the public key to the server, not the server share anything with the client.
If (b) then the client needs the shared secret to complete the signing, it will generate the JWT 'token' itself new every time it makes a request to the microservices endpoints.
So you don't give a token to a client, the client generates (or rather constructs) it's own signature (the token) using the shared secret. The server also has the shared secret and can also construct and identical signature (token) on the server without the secret ever being sent over the communications channel with the data.
You will need to find an out-of-band method for a client to obtain the shared secret because sending it in the same communication method will defeat the entire purpose if having JWT.
If (c) This is not a use case for JWT. If you do not do actual authentication on the client, then the client-side is not proven to be 'authentic' using a security characteristic that is established to be a form of an authentication mechanism. Passing a string to a client endpoints from a server is only an authentication on the server, when the client later communicates using your method it is claiming it is the 'authentic' server who 'authenticated', which it is not, it is the client..
That's why JWT is not making sense in this situation. If you do not ever what to do the authentication on the client side, then the client must be treated as unauthenticated and don't need authorisation (JWT) because they have no authentic identity established and are indistinguishable from one another.
If you do want client side authorisation, you need client side authentication first. Maybe you are having trouble with implementation of Authentication on a client, you may find OIDC easier
If App2 is the child, then App1 should dictate the auth expiration protocols. Why not just set your session expiration equal to your token expiration? Each time a user logs in to App1, decode the JWT and pass the expiration as a variable to App2.
Or set a timeout equal to moments before expiration and silently pass token refresh requests until both expirations are nearly equal.
Also, don't pass tokens openly in this manner. Instead, pass encrypted headers or cookies. Or hash tokens and save them to a secure database for query or update as needed.
One possible way to sync the JWT token expiration from the container app to the child app's session is to use a session listener on the child app that will invalidate the session when the token expires. A session listener is a class that implements the HttpSessionListener interface and can perform actions when a session is created or destroyed. You can register a session listener in your child app by adding a #WebListener annotation or by declaring it in your web.xml file.
To invalidate the session when the token expires, you can store the token expiration time as a session attribute when you create the session in the /user endpoint. Then, in the sessionDestroyed method of the session listener, you can compare the current time with the stored expiration time and invalidate the session if the token has expired. This way, the child app will not accept any requests from the session after the token expiration and will require a new token from the container app.
Example
Here is an example of a session listener class that invalidates the session when the token expires:
#WebListener
public class TokenExpirationSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
// do nothing
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
// get the session and the token expiration time
HttpSession session = se.getSession();
Long tokenExpiration = (Long) session.getAttribute("tokenExpiration");
// check if the token has expired
if (tokenExpiration != null && System.currentTimeMillis() > tokenExpiration) {
// invalidate the session
session.invalidate();
}
}
}
Here is an example of how to store the token expiration time as a session attribute in the /user endpoint:
// get the token from the query parameter
String token = request.getParameter("token");
// validate the token with the container app
boolean isValid = validateToken(token);
// if the token is valid, parse it and create the authentication object
if (isValid) {
// parse the token and get the expiration time
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
Long expiration = claims.getExpiration().getTime();
// get the username and authorities from the claims
String username = claims.getSubject();
List<GrantedAuthority> authorities = getAuthoritiesFromClaims(claims);
// create the authentication object and set it to the security context
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
// get the session and store the token expiration time as a session attribute
HttpSession session = request.getSession();
session.setAttribute("tokenExpiration", expiration);
}
Spring's SecurityContextLogoutHandler notes that the clearAuthentication flag is used to:
removes the Authentication from the SecurityContext to prevent issues with concurrent requests.
What specific issue is being prevented by removing the Authentication from the SecurityContext? Why isn't simply invalidating the session (which is the other responsibility of SecurityContextLogoutHandler) sufficient?
By not clearing the SecurityContext is the concern that a SecurityContextPersistenceFilter may preserve the current authentication to a new session id? Effectively leaving the user logged in just with a new session?
What is SecurityContextLogoutHandler?
SecurityContextLogoutHandler is a handler which implements LogoutHandler.
What SecurityContextLogoutHandler does?
It performs a logout by modifying the SecurityContextHolder.
It will also invalidate the HttpSession if isInvalidateHttpSession()
is true and the session is not null.
It will also remove the Authentication from the current
SecurityContext if clearAuthentication is set to true (default).
Is SecurityContextHolder thread safe?
Yes, it's thread safe with the default strategy (MODE_THREADLOCAL) (as long as you don't try to change the strategy on the fly). However, if you want spawned threads to inherit SecurityContext of the parent thread, you should set MODE_INHERITABLETHREADLOCAL.
Also aspects don't have any "threading logic", they are executed at the same thread as the advised method.
Credit goes to #axtavt
What is authentication in Spring Security?
Authentication: The framework tries to identify the end user with the provided credentials. The authentication can be done against a third party system plugged into Spring Security.
Let's consider a standard authentication scenario that everyone is familiar with.
A user is prompted to log in with a username and password.
The system (successfully) verifies that the password is correct for
the username.
The context information for that user is obtained (their list of
roles and so on).
A security context is established for the user
The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.
The first three items constitute the authentication process so we'll take a look at how these take place within Spring Security.
The username and password are obtained and combined into an instance
of UsernamePasswordAuthenticationToken (an instance of the
Authentication interface, which we saw earlier).
The token is passed to an instance of AuthenticationManager for
validation.
The AuthenticationManager returns a fully populated Authentication
instance on successful authentication.
The security context is established by calling
SecurityContextHolder.getContext().setAuthentication(...), passing
in the returned authentication object.
SecurityContextPersistentFilter
The name is quite explicit. The SecurityContextPersistentFilter interface purpose is to store the security context in some repository.
To achieve this task, the filter delegates the job to a SecurityContextRepository interface.
Spring provides a default implementation for this interface: org.springframework.security.web.context.HttpSessionSecurityContextRepository. This is quite self-explanatory. The repository for the security context is simply the current user HTTP session.
Below is the XML configuration for the SecurityContextPersistentFilter
<!-- Filter to store the Authentication object in the HTTP Session -->
<bean id="securityContextPersistentFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name="securityContextRepository" ref="securityContextRepository" />
</bean>
<bean id="securityContextRepository"
class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
LogoutFilter
The LogoutFilter is in charge of logging out the current user and invalidating the security context. The task of invalidating the HTTP session is again delegated to another actor, the SecurityContextLogoutHandler.
This handler is injected in the LogoutFilter constructor:
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/pages/Security/logout.html" />
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/j_myApplication_logout"/>
</bean>
<constructor-arg value="/pages/Security/logout.html" /> - it defines the URL of the logout page.
The SecurityContextLogoutHandler is injected as constructor argument at <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
The HTML URL for the logout action is define by the filterProcessesUrl parameter at <property name="filterProcessesUrl" value="/j_myApplication_logout"/>
Resource Link:
https://doanduyhai.wordpress.com/2012/01/22/spring-security-part-i-configuration-and-security-chain/
https://doanduyhai.wordpress.com/2012/02/05/spring-security-part-ii-securitycontextpersistentfilter-logoutfilter/
http://shazsterblog.blogspot.com/2014/02/spring-security-custom-filterchainproxy.html
http://docs.spring.io/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/web/context/SecurityContextPersistenceFilter.html
clearAuthentication flag was added in this commit with comment
Previously there was a race condition could occur when the user attempts to access
a slow resource and then logs out which would result in the user not being logged
out.SecurityContextLogoutHandler will now remove the Authentication from the
SecurityContext to protect against this scenario.
It fixed this issue (same issue on github). Quote:
HttpSessionSecurityContextRepository restores authentication to the session if session is invalidated from another thread if SecurityContextPersistenceFilter execution takes significant amount of time.
I am using Spring + JSF + DWR framework + GWT event service (ajax push). In any time there is at least one thread waiting at the server side for push events. This request is handled by SecurityContextPersistenceFilter which remembers the authentication at the moment of request's arriving to the server. If during the processing of this filter the session is being invalidated (by clicking logout in another tab of invalidating session by id from admin area) then HttpSessionSecurityContextRepository put the outdated authentication to the new session(which is created by JSF framework, so the session is changed during the processing of SecurityContextPersistenceFilter ).
This easily reproducable if some processing delay is inserted to SecurityContextPersistenceFilter.
SaveToSessionResponseWrapper should remember the initial HttpSession and check if the original session was invalidated so it won't set the current authentication to the new session.
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html
Storing the SecurityContext between requests
Depending on the type of application, there may need to be a strategy in place to store the security context between user operations. In a typical web application, a user logs in once and is subsequently identified by their session Id. The server caches the principal information for the duration session. In Spring Security, the responsibility for storing the SecurityContext between requests falls to the SecurityContextPersistenceFilter, which by default stores the context as an HttpSession attribute between HTTP requests. It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes. You shouldn't interact directly with the HttpSession for security purposes. There is simply no justification for doing so - always use the SecurityContextHolder instead.
Many other types of application (for example, a stateless RESTful web service) do not use HTTP sessions and will re-authenticate on every request. However, it is still important that the SecurityContextPersistenceFilter is included in the chain to make sure that the SecurityContextHolder is cleared after each request.
[Note] Note
In an application which receives concurrent requests in a single session, the same SecurityContext instance will be shared between threads. Even though a ThreadLocal is being used, it is the same instance that is retrieved from the HttpSession for each thread. This has implications if you wish to temporarily change the context under which a thread is running. If you just use SecurityContextHolder.getContext(), and call setAuthentication(anAuthentication) on the returned context object, then the Authentication object will change in all concurrent threads which share the same SecurityContext instance. You can customize the behaviour of SecurityContextPersistenceFilter to create a completely new SecurityContext for each request, preventing changes in one thread from affecting another. Alternatively you can create a new instance just at the point where you temporarily change the context. The method SecurityContextHolder.createEmptyContext() always returns a new context instance.
The SecurityContextLogoutHandler invalidates the Servlet session, in the standard Servlet way, calling the
invalidate method on the HttpSession object and also clearing the SecurityContext from Spring Security. SecurityContextLogoutHandler implements the LogoutHandler interface
Traditionally in Java web applications, user session information is managed with the HttpSession object.
In Spring Security(session clearing), at a low level, this is still the case ,
Spring security introduced new way of handling sessions or user session information.
In an application using Spring Security, you will rarely access the Session object directly for retrieving user
details. Instead, you will use SecurityContext (and its implementation class) and SecurityContextHolder
(and its implementing classes). The SecurityContextHolder allows quick access to the SecurityContext, the
SecurityContext allows quick access to the Authentication object, and the Authentication object allows quick
access to the user details.
for example following programing illustrates accesing authentication object and displaying message
#Controller
#RequestMapping("/admin")
public class AdminController {
#RequestMapping(method = RequestMethod.POST, value = "/movies")
#ResponseBody
public String createMovie(#RequestBody String movie) {
System.out.println("Adding movie!! "+movie);
return "created";
}
#RequestMapping(method = RequestMethod.GET, value = "/movies")
#ResponseBody
public String createMovie() {
UserDetails user = (UserDetails)SecurityContextHolder.getContext().getAuthentication().
getPrincipal();
System.out.println("returned movie!");
return "User "+user.getUsername()+" is accessing movie x";
}
}
once authentication is done it creates a new session is created;
Once session is created it contains information of user .on logout u need
not only to invalidate session but also need to clear the session information
by default in
`isInvalidateHttpSession(`)
checks whether session is valid or not
if session is valid
setInvalidateHttpSession(boolean invalidateHttpSession)
is called . howver invalidateing session object invalidates but session object still contains information.
to clear session information u need to call
setClearAuthentication(boolean clearAuthentication)
method thus becomes thread safe if u dont it third method it information can be retrieved at low level
I have created a basic spring security authentication using UserDetailsService and now I am able to validate user. However, I don't understand how to achieve below things:
Once a user is logged in, when next request comes how and where do I check if the request is coming from the same logged in user or other logged in user?
I know the concept of Spring interceptors where I can intercept all incoming request. But is there something in spring security that does this?
How can I start a session after logging in and store values in session for that user?
I browsed through existing answers but most of examples are for logging in.
I would appreciate if someone can give me examples.
EDIT:
I think I should use session scoped beans in order to maintain user's session contents rather than manipulating httpsession directly.
I think you really need to spend some time reading the Spring security documentation and over all JSP, servlet and MVC architecture. You have several misunderstandings,
After authentication, you don't need to start a session it was already there when the request came. Remember request.getSession()we get the session from the request and I am really NOT aware of any other way i.e. instantiating session object and assigning it to request/response. After successful authentication spring automatically sets a SPRING_SECURITY_CONTEXT attribute in session and this variable is later used to determine whether user is already authenticated or not (Spring does that for you, you don't need to use this attribute).
In spring security we set an authentication entry point which has information about login page url and FORM_LOGIN_FILTER which has information about login processing url, login success url and login failure url among few other things.Every request whose session doesn't have SPRING_SECURITY_CONTEXT and auth attribute gets redirected to login page url.
I could give the code directly but it would be great if you read at least few pages of Spring documentation here. Once you understand the concepts and are still not able to solve the problem. Edit your question with detailed problem and we will try to fix it.
At first you need to create an Authentication object using current HttpRequest as below:
public class SessionService{
public Authentication getSession(HttpServletRequest request) {
HttpSession session=request.getSession();
SecurityContext ctx= (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
Authentication auth=ctx.getAuthentication();
return auth;
}
}
Then, you can retrieve the session details from this Authentication object by passing the current HttpRequest as follows:
Authentication auth = sessionService.getSession(request);
The above auth object contains the details that you need.
I understand what #Secured DOES but not really sure HOW it does it.
Can someone explain how #Secured grabs the roles? Is it getting it from the authorities-by-username-query? Can I put any String in the params as long as it's in the database?
User's roles are stored in SecurityContext, or to be more specific in Authentication object that is stored in the SecurityContext. When you authenticate, the authentication information is loaded and stored in the security context. The roles can originate from database, depending on your configuration. In your case they are loaded using authorities-by-username-query query.
When the security interceptor processes the authorization (for instance method-level authorization using #Secured annotation) it determines whether the user should be able to access it based on the Authentication stored in the context.
To better understand what happens under the hood, you should look at the Spring Security Filter chain and Architecture section of the reference guide.
SpringSecurity provides and awesome AOP way of securing methods in
Java Application by using #Secured. Spring logically ORs the roles
listed in #Secured annotation. The collection of GrantedAuthorities is obtained from SecurityContextHolder.getContext().getAuthentication().getAuthorities()
AbstractSecurityInterceptor is the abstract class that implements
security interception for secure objects.
Obtain the Authentication object from the SecurityContextHolder.
Determine if the request relates to a secured or public invocation
by looking up the secure object request against the SecurityMetadataSource.
For an invocation that is secured (there is a list of
ConfigAttributes for the secure object invocation):
If either the Authentication.isAuthenticated() returns false, or the alwaysReauthenticate is true, authenticate the request against the configured AuthenticationManager. When authenticated, replace the Authentication object on the SecurityContextHolder with the returned value.
Authorize the request against the configured AccessDecisionManager.
Perform any run-as replacement via the configured RunAsManager.
Pass control back to the concrete subclass, which will actually proceed with executing the object. A InterceptorStatusToken is returned so that after the subclass has finished proceeding with execution of the object, its finally clause can ensure the AbstractSecurityInterceptor is re-called and tidies up correctly using finallyInvocation(InterceptorStatusToken).
The concrete subclass will re-call the AbstractSecurityInterceptor via the afterInvocation (InterceptorStatusToken, Object) method.
If the RunAsManager replaced the Authentication object, return the SecurityContextHolder to the object that existed after the call to AuthenticationManager.
If an AfterInvocationManager is defined, invoke the invocation manager and allow it to replace the object due to be returned to the caller.
Look at the source code for more understanding.
AccessDecisionManager is the interface which is implemented as AffirmativeBased, ConsensusBased or UnanimousBased orchestrates the voters and asks each in turn whether the requesting user should be let through the #Secured annotation or denied.
Please help me on this.
void setCreateSessionAllowed(boolean createSessionAllowed)
method of
org.springframework.security.web.savedrequest.HttpSessionRequestCache
class says
If true, indicates that it is permitted to store the target URL and exception information in a new HttpSession (the default). In situations where you do not wish to unnecessarily create HttpSessions - because the user agent will know the failed URL, such as with BASIC or Digest authentication - you may wish to set this property to false.
So I did not understand the description properly, also we are using a product and its documentation says setting it to false will disable the creation of anonymous user sessions.
So my question is, session creation and associating it with a request is servlet container's job. So How come using this method(setCreateSessionAllowed) will not create a session.
Please validate my understanding, is it correct or not. also
setCreateSessionAllowed(false), will JSESSIONID be created or not?
The HttpSessionRequestCache saves the last URL requested by the client in a user session.
This is used by spring security, when it redirects you to a login page, to restore the url after a successful login.
In case of basic or digest authentication is directly authenticated or asked to resend the request with the credentials. Therefore URL caching is not necessary.
If setCreateSessionAllowed is set to true, it will by default create a session to store the last url.
If set to false it will only support this feature if a session is already is created.
If no url is stored, spring security will use the default target URL supplied in the spring security configuration.
As for your last question, it is not directly obvious to me how it will affect the anonymous login feature of your mentions product.