I am trying to figure out where the timed-logout function of Spring's light security is located, and how to edit it to use my custom logout method.
My understanding is that there does exist an editable timed-logout function, I have just thus far been unable to find it, and when/if I do, I am unsure how to make it use my logout sequence.
Thanks,
MirroredFate
EDIT FOR CLARITY:
It is a web/servlet application. I am using acegi security.
I am using the Spring session timeout right now:
In web.xml:
<session-config>
<session-timeout>5</session-timeout>
</session-config>
I need a way to execute some code when this timeout occurs. However, I have NO idea how to do this.
If I am unable to execute code using this method, my understanding is that acegi has a way to make a session timeout; however, I have no idea how to do that either. I already have the ability to execute the code on a normal logout using acegi:
<security:logout invalidate-session="true"
success-handler-ref="Logout"
logout-url="/logout.html" />
</security:http>
So, essentially, how do I do this same thing either with an acegi timed logout or when a session timeout occurs?
The HttpSessionListener might be what you are looking for. The problem with depending on Spring's session management is that if a user simply closes his browser without logging out, the invalid-session-url will never be reached (because they never make another request).
Something like this:
public class MySessionListner implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent se) {
return; //or maybe do something, depends on what you need
}
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
//do whatever you need to do
}
}
Then in web.xml:
<listener>
<listener-class>com.foo.MySessionListener</listener-class>
</listener>
That way your code will be called every time a session is destroyed, not just when a user tries to access a page after timing out. Hope that helps.
The session timeout period is managed by your application server (just as you have it now in the web.xml). The handling for what happens when the session timeout occurs can be specified in Spring. For instance, Spring 3.0 can specify what page to redirect the user to when they make a request after their session has been invalidated. See below.
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<sec:http auto-config="true" access-denied-page="/login">
<sec:session-management invalid-session-url="/session-timeout" />
<sec:anonymous enabled="true" />
<sec:intercept-url ... />
<sec:form-login ... />
<sec:logout invalidate-session="true" logout-success-url="/login" />
</sec:http>
<!-- Other bean declarations -->
</b:beans>
Related
This question already has answers here:
Read out incoming certificate in Tomcat
(2 answers)
Closed 7 years ago.
Since google killed the NPAPI plugin and we cannot load applets in Chrome our client certificate based web app authentication scheme is not working anymore.
While looking for alternatives I found out that, because we are using SSL on our private content, there is a way to automatically ask for an SSL client certificate, by enabling SSL client authentication on the web server (In this case Tomcat).
My question is: Once I've enabled Tomcat's SSL client authentication to request this certificate from the client browser's personal certificate store:
How can I obtain the certificate info on my JSF web application so I can register a user with that info on first login and associate it with a user id?
(I am assuming that I don't need to worry about fake or expired certificates since a properly configured Tomcat will reject them, so I don't need to bother with the authentication / certificate validation, but just retrieve the info: Tomcat handles rejections / authentication errors by redirecting to the source page with an error code)
I am going to describe a procedure to validate against spanish DNIe which uses client certificate using JSF 2.2 and Spring Security 4.0, although it is possible to authenticate without using Spring Security.
You said you have enabled Tomcat's SSL client authentication, so I guess it implies you have already configured keyStore with ROOT certificate. If you don't I can provide you with valid instructions for Tomcat 7.
So, once Tomcat is properly configured to require client certificate, and once the handshake has finished, this is what you have to do in your application to read client certificate:
Configure dependencies in pom.xml
Add following dependencies to pom.xml:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
It is worth to say that Spring Security 4.0.1 is bound to Spring 4.1.X
Configure web.xml to delegate in Spring Security
Tell servlet container, security is delegated into Spring Security
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
It must ask for a client certificate:
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate</realm-name>
</login-config>
Configure Spring Security filter
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
Don't forget to add spring security file descriptor into contextConfigLocation context param.
Configure Spring Security
Following is a large file which configure Spring Security to validate against client-certificate.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userDetailsService" class="your.own.UserDetailService">
<property name="dao" ref="userDao" />
</bean>
<bean id="dniPrincipalExtractor" class="your.own.DniePrincipalExtractor">
</bean>
<bean id="x509Filter" class="org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter">
<property name="authenticationManager" ref="authManager" />
<property name="principalExtractor" ref="dniPrincipalExtractor" />
</bean>
<bean id="preauthAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="authenticationUserDetailsService" />
</bean>
<bean id="authenticationUserDetailsService"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<sec:http pattern="/css/**" security="none" />
<sec:http pattern="/error/**" security="none" />
<sec:http pattern="/icons/**" security="none" />
<sec:http pattern="/imgs/**" security="none" />
<sec:http
auto-config="true"
use-expressions="true"
entry-point-ref="forbiddenAuthEP">
<sec:intercept-url pattern="/*" access="permitAll" />
<sec:intercept-url pattern="/xhtml/**" access="hasRole('ROLE_BASIC')" />
<sec:custom-filter ref="x509Filter" position="X509_FILTER"/>
</sec:http>
<sec:authentication-manager alias="authManager">
<sec:authentication-provider ref="preauthAuthenticationProvider" />
</sec:authentication-manager>
</beans>
This file creates a security context that requires login by means of a x509Filter. This filter needs a
dniPrincipalExtractor which a class you need to find and extract dni from user certificate.
userDetailsService which knows how to find the user into data base, using userDao.
With this configuration, once a client certificate is received, a pre-authentication service acts to extract DNI and load user from data base (or whatever) building the
This code implies you have to build three classes of your own:
your.own.UserDao
your.own.UserDetailService it has to implements org.springframework.security.core.userdetails.UserDetailsService. Here you have to retrieve roles or groups assigned to user to build List<GrantedAuthority> for the user and create a valid org.springframework.security.core.userdetails.User.
your.own.DniePrincipalExtractor it has to implements org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor
Sample UserDetailService
package your.own.package;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import your.own.UserDAO;
public class UserDetailService implements UserDetailsService {
private UserDAO dao = null;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String error = null;
UserDetails result = null;
if(StringUtils.isNotEmpty(username)){
if(dao.findById(username) != null){
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_BASIC", "ROLE_ADMIN");
result = new org.springframework.security.core.userdetails.User(username, "", authorities);
}
}else{
error = "No se ha especificado login para el usuario.";
}
if(result == null){
if(StringUtils.isEmpty(error)){
error = String.format("No se encuentra ningĂșn usuario con login %s", username);
}
throw new UsernameNotFoundException(error);
}
return result;
}
}
For simplicity I put roles by hand, obviously you will have to change this.
Get authenticated user
Now you can get authenticated user this way, in session bean or whatever
SecurityContextHolder.getContext().getAuthentication().getName()
If you need, I can provide you with more information, but I didn't want to write a very large answer. Things that have been not covered in this answer:
DNI principal extractor: If you search google you will find several implementations, but I can provide you one.
OCSP validation: You have to manage cert validation against Policia Nacional OCSP server. I can tell you how to do.
JSF Tag Libs to ask for user grants.
Tomcat full configuration.
Implementing a fallback mechanism to launch a form validation page if there is no client certificate. It is possible with Spring Security
My first implementation didn't use Spring Security, but as far I was concern with the need to provide a fallback mechanism, I move forward Spring Security, although I didn't show you how to do here, for simplicity.
Hope it helps!
Recently I am using Spring Security basic authentication for my REST services.
Below is the security xml configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http pattern="/rest/**" create-session="never" use-expressions="true">
<security:http-basic />
<security:intercept-url pattern="/rest/auth/**" access="isAuthenticated()"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
<security:authentication-provider user-service-ref="userDetailsService">
<security:password-encoder hash="sha-256" />
</security:authentication-provider>
</security:authentication-manager>
<security:global-method-security jsr250-annotations="enabled" pre-post-annotations="enabled"/>
</beans>
Spring framework and Spring Security that I use:
<springframework.version>4.1.0.RELEASE</springframework.version>
<spring.security.version>3.2.5.RELEASE</spring.security.version>
I map my REST services to URL prefixed with "rest/" and when I access the URL for the first time, the browser prompt the username and password field of basic authentication. I fill it with the right credential and my controller accessed successfully.
However if I try to access the same URL with browser again, it will not prompt me the username and password field of basic authentication again and directly access the URL.
I expect that browser always prompt me with basic authentication because I set create-session attribute to never.
So, am I missing something?
Browser caches credentials. Sometimes clearing the cache doesn't help. The only reliable way how to fake it is use Chrome's incognito window (Ctrl+N). But one prompt per one incognito window. So you need new incognito window when you want to enter them again.
This is the only way I found to test basic authentication manually.
From the javadoc for create-session. I think your application is creating a sessions and that session is being used.
Attribute : create-session Controls the eagerness with which an HTTP
session is created by Spring Security classes. If not set, defaults
to "ifRequired". If "stateless" is used, this implies that the
application guarantees that it will not create a session. This
differs from the use of "never" which mans that Spring Security will
not create a session, but will make use of one if the application
does.
Data Type : string Enumerated Values :
- ifRequired
- always
- never
- stateless
You should try using
"stateless"
instead of
never
I am trying to get my spring + hibernate + spring-security and tiles2 - "HelloWorld" application to work, following this guide (its in german unfortunately).
My problem is that I get a "404" error message when logging into my application. Redirection to the login page works as intended, but I can't reach "http://localhost:8080/App/j_spring_security_check" when I hit the login button.
My web.xml looks this way:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/defs/applicationContext.xml
/WEB-INF/defs/applicationContext-security.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
and applicationContext-security.xml file looks this way ...
<http use-expressions="true">
<intercept-url pattern="/index.html" access="permitAll" />
<intercept-url pattern="/timeout.html" access="permitAll" />
<intercept-url pattern="/redirect.html" access="permitAll" />
<intercept-url pattern="/media/**" access="permitAll" />
<intercept-url pattern="/includes/**" access="permitAll" />
<intercept-url pattern="/office/**" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/office/admin/**" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/index.html"
authentication-failure-url="/index.html?login_error=1"
default-target-url='/office/kunden.html'
always-use-default-target='true'
/>
<logout logout-success-url="/index.html" />
<remember-me />
<session-management invalid-session-url="/index.html">
<concurrency-control max-sessions="2" error-if-maximum-exceeded="true" />
</session-management>
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="mysqldataSource"
authorities-by-username-query="select username, authority from benutzer where username = ?"
users-by-username-query="select username, password, enabled from benutzer where username = ?"/>
</authentication-provider>
</authentication-manager>
The database connection seems to be O.K.
I would be very glad if someone could give me a hint on that, because I already did a lot of googling, but didn't find a solution yet.
I use spring 3.1 and tomcat 7.0.23
I would check two things:
Request dispatch
Spring-security config
To check request dispatch just make sure that your application is accessible in the servlet container in the first place. Meaning, you have mentioned http://localhost:8080/App/j_spring_security_check. Is your application accessible under that URL? Does http://localhost:8080/App show proper content (HTTP 200)?
Also make sure that dispatcher servlet is configured properly. In tutorial you have provided, there is this section:
<!-- Spring Hauptteil -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
If you have not provided it in your web.xml, then your request might not even be dispatched properly before it ends up being examined via spring-security.
If this doesn't help you, try this.
Following documentation, the minimal configuration should be enough to check if your setup is correct. If you have followed tutorial, you might make some minor mistake (typeo, for instance) that will cause spring-security not to launch properly. Then it is easy to skip some error info in logger output.
I suggest you do the following.
Change your applicationContext-security.xml to support minimal configuration provided in documentation.
Launch the application and go to http://localhost:8080/App/j_spring_security_check
If you get proper response - try modifying config until you are done.
Point to learn
What DelegatingFilterProxy (defined in web.xml) really does is delegating request to some other filter managed by Spring's IoC. This filter is being defined in applicationContext-security via security namespace. If this won't work for some reason, the filter will not be initialized, and you may end up in seeing http 404 regardless the fact, that the rest of application starts properly.
Uffff, lots of text ;)
Your configuration looks ok. One thing that can case the 404 is if the default-target-url='/office/kunden.html' points to an controller or view that does not exist.
Check that the url /office/kunden.html works -- therefore deactivate the security stuff (just add <security:intercept-url pattern="/**" access="permitAll" />) and try it.
An other thing that may goes wrong, is that the tutorial is for spring 3.0 but not spring 3.0. I would not expect that this is the cause, but give it a try and downgrade.
For those people who face the same symptoms, but for a different situation, those who are behind a load balancer which does SSL offloading, the following answer might put you in the right direction. I had a similar problem and it turned out that the incoming request was handled correct, however as a response spring security sends a redirect to an absolute URL which is defined by the default-target-url attribute (starting with http instead of https)
<security:form-login login-page="/login.jsp" default-target-url="/index.jsp" authentication-failure-url="/login.jsp?error=true" />
Now the client browser tries to open the redirected location on http, fails on the loadbalancer (which only accepts https traffic) and reports a 404 NOT FOUND
We solved this problem by adding the following mod_header directive for all incoming requests on port 443 (https) in the load balancer:
RequestHeader set X-Forwarded-Proto "https"
The will add an extra header. If you run an application server like Jetty, it will recognize this header and translate the incoming request. (see http://www.gossamer-threads.com/lists/apache/users/407272)
I use Spring and Spring Security 3 at my application. All my clients side are static HTML files. I have a navigation bar that includes buttons like:
List
Edit
Delete
Update
When a user clicks any of them another page loads at bottom. Users have roles at my application. Some users do not have edit and delete authorization, while others do. That buttons should be visible to users which have the authorization. If a user doesn't have edit the correct permission he/she must not see the edit button. I have the buttons defined in an HTML file: navigation.html. I figured out that: there will be many navigation.html files. One of them includes all buttons(for admin) one of them just includes list button. If a user requests that navigation.html I want to send the correct one. So I can have that ability:
<logout logout-url="/j_spring_security_logout" logout-success-url="/login.html"/>
similar to that user will request that file from an URL(as like /navigation). There will be a controller to handle it so will return any of that navigation files.
Does that design sound correct? If so, how can I implement that? Any other simple solutions are welcome I am new to Spring and Spring Security.
For general Spring Security use, you don't need to write your own code to enable authorization. I generally configure Spring Security in XML to control access at a gross level to various resources based on Roles. Then, I annotate the controllers and/or handler methods to restrict more precisely.
Example:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns:security="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<security:global-method-security secured-annotations="enabled">
</security:global-method-security>
<security:http auto-config="true" disable-url-rewriting="true">
<security:intercept-url pattern="/*.do" access="ROLE_USER" />
<security:intercept-url pattern="/index.jsp" access="ROLE_USER" />
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:intercept-url pattern="/login.jsp" filters="none" />
<security:form-login login-page="/login.jsp" />
<security:logout />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:jdbc-user-service data-source-ref="my-ds"/>
</security:authentication-provider>
</security:authentication-manager>
</beans:beans>
And then in the Controller:
#Secured({"ROLE_SPECIAL_USER"})
#RequestMapping("/somespecial.do")
Within a JSP:
<%# taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<authz:authorize ifAnyGranted="ROLE_SPECIAL_USER">
...some special JSP code...
</authz:authorize>
Based on your using static HTML, I would think that the design you specify would be reasonable.
Have a Controller that maps to navigation.html, and it would simply look at the granted authorities of the current user and return the correct static html view name for the html file that has all (and only) the appropriate controls.
I'm some what lost as to why spring isn't enforcing the #Secured("ROLE_USER") on my service interface. My controllers are established using annotations.
An example of my service Interface
public interface MyServiceManager {
#Secured("ROLE_USER")
public void delete(int cid);
#RolesAllowed({"ROLE_USER"})
public Contact getContact(int contactId);
}
my security-context:
<global-method-security secured-annotations="enabled" jsr250-annotations="enabled">
</global-method-security>
<http auto-config="true" >
<intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR" />
<intercept-url pattern="/addcontact**" access="IS_AUTHENTICATED_REMEMBERED" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<concurrent-session-control max-sessions="1"
exception-if-maximum-exceeded="true"/>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
<logout logout-success-url="/welcome.do" logout-url="/logout"/>
</http>
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
</user-service>
</authentication-provider>
Do you have the statement
<global-method-security secured-annotations="enabled" jsr250-annotations="enabled" />
in the same configuration file as the one you defined the MyServiceManager bean? I had the same problem until I turned on debug for org.springframework, and noticed that spring security was only applied on the same file as the ones where global-method-security was defined in.
In my case, the exact location of this statement:
<global-method-security secured-annotations="enabled" >
proved to be very important. Make sure that you put it after you declare which classes should be scanned and used as controllers.
<context:component-scan base-package="com.test.controller" />
This is the way to make sure that the #Secured annotations will also get into the game
After doing more research on this problem I came to the following conclusion/solution. I'm not sure if it's 100% correct..but it works.
I put all of my configuration in the dispatcher-servlet.xml file. So instead of having a disptacher-servlet.xml and application-context.xml. The dispatcher-servlet.xml is loaded by the application (contextConfigLocation). Within the dispatcher-servlet.xml I import my security-context.xml and datasource-context.xml. Afer that, everything works.
I had this same problem. Using the information from Kent Lai's reply here, I was able to fix it.
I put the <global-method-security> element in my app-servlet.xml but kept the security definitions separate in security.xml, where web.xml has contextConfigLocation for app-servlet.xml and security.xml.
Works like a charm now!
Try putting the annotations on the implementation class instead of the interface and see if that works. I ended up doing that on a recent project because I was also using the #Transactional attribute on my service layer, and the Spring docs recommend putting those on the class and not the interface. I don't know if the same issue might apply to #Secured, but I wanted to keep the annotations in the same place. See the Spring Docs
Regarding Kent Lai's answer...that is a good idea...make sure that your security config file is actually being included by Spring.
Did you use something like this in your web.xml
<servlet>
<servlet-name>name</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I'm not sure why, but if I use the DispatcherServlet I was not able to enforce Security annotations
I had this same problem.
After I added:
<context:annotation-config />
in my spring-security.xml file it disappeared.
Hope this will help someone :)