I'm trying to make a java web application truly stateless (although still using basic authentication) but since now a JSESSIONID cookie is always generated by our servlet container (Tomcat).
This my stack:
Java: 1.8
Spring: 4.1.6.RELEASE
Spring Security: 4.0.2.RELEASE
Tomcat: 7.0.93
We use XML configuration, so this is my security config file, where I used the STATELESS option for session creation:
<beans:bean id="requestCache" class="org.springframework.security.web.savedrequest.NullRequestCache" />
<http use-expressions="true" create-session="stateless" pattern="/api/**">
<request-cache ref="requestCache"/>
<csrf disabled="true"/>
<!-- REST ENDPOINTS PATH BASED -->
<intercept-url pattern="...."/>
<intercept-url pattern="...."/>
</http>
As documented in this response this should be enough to ensure that Spring Security won't create a session, but other parts of my application could still create one.
The question is: how do I track who's requesting the session creation?
Basically what I'm trying to do is adapt a backend used by a stateful java application, to be consumed in a stateless way by other client applications that will only make calls to a particular path (/api/**) as detailed in my security config file.
This stateful part uses some beans that are session-scoped; I need to use those same beans but in a request-scope way, thus my need to ensure that a JSESSIONID cookie is never created.
Trying for example to disable cookies altogheter in Tomcat (or in the browser) accomplishes this, so I'm trying a way to do it directly with Spring.
If you want to be sure sessions are not created, create a filter and wrapper the HttpServletRequest with a class the blocks/fails/ignores the getSession(...) calls.
Related
I needed to change JSESSIONID's domain to ".something.com" in a context.xml file:
<Context path="/test" sessionCookiePath="/" sessionCookieDomain=".something.com" useHttpOnly="true" />
After that, when I perform a httpSession.invalidate() the session is reset but JSESSIONID value does not change.
I'm using Java 7, Spring MVC and Tomcat 7. I also tried to remove the JSESSIONID cookie manually, but it seems that Tomcat or Spring are not letting I change its value.
This may difficult troubleshooting on my system. I'd like to know if it's possible to change this behavior either on Spring or in Tomcat.
I found the problem in Tomcat's documentation:
"Note: Once one web application using sessionCookiePath="/" obtains a session, all subsequent sessions for any other web application in the same host also configured with sessionCookiePath="/" will always use the same session ID. This holds even if the session is invalidated and a new one created. This makes session fixation protection more difficult and requires custom, Tomcat specific code to change the session ID shared by the multiple applications."
Source: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
The issue is related to cookie path, and not with domain
Assuming you're using Spring Security, you can configure the session logout handler to delete the cookie for you.
...
<logout delete-cookies="JSESSIONID">
...
Or, in Java configuration, in a WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.logout()
.deleteCookies("JSESSIONID");
}
If you're not using Spring Security, you can probably install a Filter object into Spring's existing filter chain to delete the Set-Cookie header in outgoing requests whose sessions have been invalidated (or on whatever condition you specify, at that point). This is more or less what Spring Security's logout handlers do, anyway.
i have the following setup.
server1: i am using spring security v3.1.4 in one application. this application is running on its own server (e.g. tomcat at server1.mydomain.com).
server2: i have another 3rd party web application running on a different server (e.g. tomcat at server2.mydomain.com). this applicaton is pluggable in that it allows me to install jars to dynamically modify behavior at runtime. its purpose is to serve content.
users and resource access are managed on server1. content is delivered by server2. on server2, if a resource request comes in, i need to ask server1 if the user is logged in. is this possible to do with spring security? i imagine i would pass in the username with the resource request (e.g. http://server2.mydomain.com?getFileId=1&username=johndoe#gmail.com).
i read a little bit on the spring security website and SSO seems to be the way to go (e.g. using Central Authentication Service). but that seems like an overkill. our architecture already has several servers running.
integration (e.g. the webapp using spring security)
media (e.g. the 3rd party webapp)
elastic search (a cluster)
mysql (a cluster)
if possible we would like to have a minimalist system (but our system isn't small, given our investments and assumptions using rdbms/IR clusters).
any help is appreciated.
One possible way of achieving this - although it's not something I've tried myself - could be to expose Spring Security's SessionRegistry in server1 via a simple REST based controller. That would then allow server2 to remotely query authenticated users in server1 by making a simple HTTP GET request.
It's probably worth having a read of the Session Management section of the Spring Security docs to determine how to access the SessionRegistry. The basic setup I think is to specify a <session-management> tag inside the <http> section of your config on server1.
<security:session-management>
<security:concurrency-control session-registry-ref="sessionRegistry"/>
</security:session-management>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
You would also need to add a listener to the web.xml of server1
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
The controller that you would need to create on server1 could then be autowired with the SessionRegistry
#Autowired
private SessionRegistry sessionRegistry
From there, you can use sessionRegistry.getAllSessions() to determine whether a username (principal) passed in a request to the controller is logged in on server1.
More of an idea than a concrete answer - but may give you an avenue to explore.
I have a question about Spring Security HTTPS redirect strategy.
First of all, the configuration:
Spring 3 Java Web App
tomcat after apache connected with jk
Spring Security 3
Using a configuration where the client connects directly to tomcat (i.e. no jk), I see that the https is managed using secure port redirect by configuring the security.xml file as follows:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
...
</http>
now, here's the question: using tomcat after apache (with jk) the secured section is on a different host so that I have the following
http (not secured host): www.myhost.com
https (secured host): ssl.myhost.com
I'd like to know if is it possible to configure spring security to redirect to the secured host in case of https is required.
I see that there's a port-mapping configuration availbale, but I don't see something similar for the host.
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</http>
Thanks in advance.
PS. the twofold hosts configuration is imposed by the hosting service.
The implementation of the default behavior is a simple redirection to the same URI with https prefix, which is done in RetryWithHttpsEntryPoint invoked by the SecureChannelProcessor. You could easily implement the desired behavior by writing your custom AbstractRetryEntryPoint that not only changes the protocol, but also the host name.
Once you've implemented that class, configure the ChannelProcessingFilter with it, based on the sample given in its javadoc: http://static.springsource.org/spring-security/site/docs/3.1.3/apidocs/org/springframework/security/web/access/channel/ChannelProcessingFilter.html
Is there a way to force my Java EE application to use j_security_check for authentication, but not authorization? I want to do authentication through a Standalone LDAP Repository, but I want to do programmatic authorization in my application.
Specifically, I do not want to do any configuration around Authorization (user role mapping to groups) within the Websphere Application Server admin console.
For example:
User logs in with 'user1' and password 'password1' which are correct
Websphere finds this person in LDAP, knows they are who they say they are (Authentication)
I check from a file on the file system if this user is authorized or not (Authorization)
I suggest using Spring Security for this. We have implemented this exact pattern in WebSphere. The trick is to use the pre-authentication mechanism that Spring Security provides and then define only the authorization rules in the configuration.
<http>
<session-management session-fixation-protection="none"/>
<custom-filter position="PRE_AUTH_FILTER" ref="preAuthenticationFilter"/>
<intercept-url pattern="/j_security_check" filters="none"/>
<intercept-url pattern="/ibm_security_logout" filters="none"/>
<!-- put authorization intercept-url elements here... -->
</http>
You have to define some other beans as well, such as the pre-authenticated filter and a custom entry point, but that's documented here: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/preauth.html
Have a look at Spring Security. The concepts of authentication and authorisation are split.
Authentication is handled by an AuthenticationProvider (an LdapAuthenticationProvider implementation is part of spring). The authentication provider delegates to fetch details of the user, which includes a list of GrantedAuthority objects which can represent a user's permissions.
LdapAuthenticationProvider by default attempts to get granted authorities from the directory, so you will need to provider your own LdapAuthoritiesPopulator implementation which loads from the file system.
Java EE Security can be split as well. It depends on your container's implementation however.
Since you mention websphere this document might be of interest.
I have web application and two domains for it - example.com and example.ru
example.com - for international
example.ru - for local country
My web app using spring security for authorization users but if user login through example.com on example.ru he isn't logged.
How can do that if user login through example.com or example.ru he will be logged on both domains?
PS: BTW my web application use authorization through OpenID and OAuth
As mentioned you need a single sign on solution, Cloudseal provides a spring security extension which includes a spring namespace so you just need to do something like:
<security:http entry-point-ref="cloudseal">
<security:intercept-url pattern="/protected/user.do" access="IS_AUTHENTICATED_FULLY" />
<security:intercept-url pattern="/protected/admin.do" access="ROLE_ADMIN" />
</security:http>
<cloudseal:sso endpoint="http://cloudseal.com" entry-point-id="cloudseal" app-id="quickstart">
<cloudseal:keystore location="WEB-INF/keystore.jks" password="nalle123">
<cloudseal:key name="apollo" password="nalle123" />
</cloudseal:keystore>
<cloudseal:metadata location="WEB-INF/idp.xml" />
</cloudseal:sso>
See www.cloudseal.com/platform/spring-security-single-sign-on
While this type of functionality is by no means trivial to achieve, it is in fact possible without modifying Spring.
The actual code is too large to post, so I'll try to outline the basic principle and leave the coding to you.
Extend Spring's SavedRequestAwareAuthenticationSuccessHandler
and implement functionality to serialize and write the
Authentication object to a Session cookie with a global scope. See
documentation for the authentication-success-handler-ref attribute
in Spring's <sec:http> tag for more information on how to wire
this up. (Note: If the problem were sso across multiple web apps on
the same domain, you could of course limit the cookie scope to the
current domain).
In all your web apps, add to web.xml a <filter> definition
named springSecurityFilterChain and class
org.springframework.web.filter.DelegatingFilterProxy and a
<filter-mapping> for the filter with a URL pattern of /* You don't have to create the actual bean, Spring Security provides a default implementation for you.
In all your web apps, add to web.xml a <filter> definition
named singleSignonAuthenticationFilterChain with class
org.springframework.web.filter.DelegatingFilterProxy and a
corresponding <filter-mapping> for the filter with a URL pattern
of /*
Now you add a new bean called
singleSignonAuthenticationFilterChain, which should point to a
class that implements Filter. In the doFilter() method, check if
there is a session attribute called SPRING_SECURITY_CONTEXT. If
there is, then we are already logged in. Otherwise, take the
serialized Authentication token, deserialize it and use
SecurityContextHolder.getContext().setAuthentication(authentication)
to authenticate the user with Spring. Also remember to
session.setAttribute("SPRING_SECURITY_CONTEXT",
SecurityContextHolder.getContext()) or the authentication will take
place each time, which is unnecessary.
A twist to (4) is that if you find out that there is no attribute called SPRING_SECURITY_CONTEXT, then it could be because the user has just logged out from the current web application. In this case he must be logged out globally, so you want to remove the cookie containing the serialized authentication token in this case.
It's kind of complex to write up in a one page summary, but I hope you get the general idea. We currently have this implemented in a complex application consisting of multiple web applications, and it works nicely.
It's impossible without modifying spring security code. I did it sometimes ago but is very hard to maintenance
Cas is the easeiest way to this in java world.
http://www.jasig.org/cas