Spring Security doesn't remember authenticated users - spring-mvc

I am using spring-security with a Spring-MVC application. Currently I am able to login user, password hashing and all is working beautifully. Whenever I need the authenticated person object, I retrieve the name via Spring security, then by accessing database I get the user object. All good till there. Now when I load the application first time, I log in, I close the tab, and I again give the application address, I would like to check if the user is authenticated and redirect. This is where I am having problem. I went through many tutorials on net, and I am almost doing the same. Kindly have a look at what I am doing wrong. Thank you for your time.
Controller :
#RequestMapping(value = "/", method = RequestMethod.GET)
public String listPersons(Model model) {
boolean id = isAuthenticated(); // Here I am calling the function I have written to see if I am logged in
if(id){
return "redirect:/canvas/list";
} else {
model.addAttribute("person", new Person());
model.addAttribute("listPersons", this.personService.listPersons());
model.addAttribute("notices",new Notes());
model.addAttribute("canvases",new Canvas());
return "person";
}
}
isAuthenticatedFunction :
private boolean isAuthenticated(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication == null){ return false;}
if(authentication instanceof AnonymousAuthenticationToken) {
return false;
} else {
return true;
}
}
Security-application-context.xml
<import resource="servlet-context.xml" />
<!-- Global Security settings -->
<security:global-method-security pre-post-annotations="enabled" />
<security:http pattern="/" security="none" />
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/" default-target-url="/canvas/list" always-use-default-target="false" authentication-failure-url="/login?error" />
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService" token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:intercept-url pattern="/canvas/list" access="hasRole('ROLE_USER')" />
<security:logout logout-success-url="/" delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout" />
<security:port-mappings>
<security:port-mapping http="80" https="443"/>
</security:port-mappings>
<security:session-management session-fixation-protection="newSession" invalid-session-url="/invalidSession.html">
<security:concurrency-control max-sessions="3" error-if-maximum-exceeded="true" expired-url="/sessionExpired.html"/>
</security:session-management>
</security:http>
<!-- queries to be run on data -->
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="key" value="_spring_security_remember_me" />
<beans:property name="tokenRepository" ref="jdbcTokenRepository"/>
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
</beans:bean>
<!--Database management for remember-me -->
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
</beans>
To retrieve logged in user, I am using the method mentioned below, which works like a charm right now.
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication == null){
System.out.println("User is not authenticated");
return null;
} else {
Person person = personDAO.findPersonByUsername(authentication.getName());
return person;
}
}

Related

Get current logged in user id

I want to get current logged in user id and pass it to the view. Users login with username and password.
spring-security.xml
<beans:beans xmlns="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.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/panel/**"
access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login login-page="/login" login-processing-url="/j_spring_security_check"
authentication-failure-url="/login?error" username-parameter="username"
password-parameter="password" authentication-success-handler-ref="authSuccessHandler" />
<logout logout-url="/j_spring_security_logout" logout-success-url="/" />
<!-- enable csrf protection -->
<csrf />
</http>
<beans:bean id="authSuccessHandler" class="com.terafast.tem.AuthSuccessHandler" />
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,enabled from users where username=?"
authorities-by-username-query="select u1.username, u2.role from users u1, user_roles u2 where u1.role_id = u2.role_id and u1.username =?" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Controller
#RequestMapping(value = "/request/create", method = RequestMethod.GET)
public String displayVacationRequestPage(Model model) {
Vacation vacation = new Vacation();
model.addAttribute("vacation", vacation);
return "panel/createRequest";
}
Could someone explain how can I do that?
You can add a simply mapping method
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
If the /user mapping is reachable it will return the currently authenticated user. Consider annotating your response with #ResponseBody, depending on how you have set up your controller and how you plan to use it in your view.
For passing a particular property you can do something like
#RequestMapping(value="/username", method = RequestMethod.GET)
public String printUser(ModelMap model, Principal user) {
String name = user.getName(); //get logged in username
model.addAttribute("username", name);
return "panel/createRequest";
}

Freemarker template error handling with Spring MVC

I am using Freemarker template with Spring MVC. Is there a way to return HTTP Status 500 if there is any error rendering the template?
Currently I am using attempt block to handle error, but would like to throw Internal Server error and allow web server to redirect to a default error page
<#attempt>
attempt block
<#recover>
recover block
</#attempt>
Below is my servlet-context.xml
<beans:bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<beans:property name="cache" value="true" />
<beans:property name="prefix" value="" />
<beans:property name="contentType" value="text/html; charset=UTF-8" />
<beans:property name="suffix" value=".ftl" />
<beans:property name="exposeSessionAttributes" value="true" />
</beans:bean>
Below change in servlet-context.xml resolved the issue. Now it throws HTTP 500 error which is intercepted by web server and I am able to mast the message by redirecting to pre-defined error page.
<beans:bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<beans:property name="templateLoaderPath" value="/WEB-INF/ftl/" />
<beans:property name="freemarkerSettings">
<beans:props>
<beans:prop key="template_exception_handler">rethrow</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
This is the java-based equivalent configuration:
#Bean
public FreeMarkerConfigurer freemarkerConfig() throws TemplateException {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/views/ftl/");
Properties settings = new Properties();
settings.setProperty(freemarker.template.Configuration.TEMPLATE_EXCEPTION_HANDLER_KEY, "rethrow");
freeMarkerConfigurer.setFreemarkerSettings(settings);
return freeMarkerConfigurer;
}
A simple way to change the mode from "debug" to "rethrow" is to configure it in your application.properties:
spring.freemarker.settings.template_exception_handler=rethrow

Spring Security retrieve current user object out two methods implementing UserDetails

I am working on a Spring-MVC project where I am using Spring-Security for authentication and other security features. Now the Project is divided into 2 parts, one is for personal login and another is group login.
For both of them, I use different database tables. But Java classes for both the tables have one instance of UserDetails and userDetailsService implemented.
Now when the user logs-in either from the personal account or from group account, I would like to extract the currently logged in users object from either of the class. This way, I would know if its a group user logged in or a personal account user logged in. Kindly let me know what should I do?
security-application-context.xml :
<security:http create-session="ifRequired" use-expressions="true"
entry-point-ref="loginUrlAuthenticationEntryPoint"
auto-config="false" disable-url-rewriting="true">
<security:logout logout-success-url="/" delete-cookies="JSESSIONID"
invalidate-session="true" logout-url="/j_spring_security_logout"/>
<security:custom-filter ref="CustomUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER" />
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
</security:http>
<bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.do?error"/>
</bean>
<bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.do"/>
</bean>
<bean id="authenticationManagerForPersonal" class="com.journaldev.spring.utility.CustomDAOAuthenticationProvider">
<constructor-arg index="0" value="org.springframework.security.authentication.UsernamePasswordAuthenticationToken"/>
<property name="userDetailsService" ref="LoginServiceImpl"/>
<property name="passwordEncoder" ref="encoder"/>
</bean>
<bean id="authenticationManagerForGroup" class="com.journaldev.spring.utility.CustomDAOAuthenticationProvider">
<constructor-arg index="0" value="com.journaldev.spring.utility.CustomUsernamePasswordAuthenticationToken"/>
<property name="userDetailsService" ref="GroupLoginServiceImpl"/>
<property name="passwordEncoder" ref="encoder"/>
</bean>
<bean id="CustomUsernamePasswordAuthenticationFilter" class="com.journaldev.spring.utility.CustomUsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="authenticationSuccessHandler" ref="redirectRoleStrategy"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="authenticationManagerForPersonal"/>
<security:authentication-provider ref="authenticationManagerForGroup"/>
</security:authentication-manager>
<bean id="redirectRoleStrategy" class="com.journaldev.spring.utility.RoleBasedAuthenticationSuccessHandler">
<property name="roleUrlMap">
<map>
<entry key="ROLE_USER" value="/person.do"/>
<entry key="ROLE_GROUP" value="/group.do"/>
</map>
</property>
</bean>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
Person.Java (Personal accounts model class) :
#Entity
#Table(name="person")
public class Person implements UserDetails{
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "person_seq_gen")
#SequenceGenerator(name = "person_seq_gen",sequenceName = "person_seq")
private int id;
// other values
}
GroupMember.java (Group Account members model)
#Entity
#Table(name="groupmembers")
public class GroupMembers implements UserDetails {
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_GROUP");
#Id
#Column(name="memberid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "groupmembers_seq_gen")
#SequenceGenerator(name = "groupmembers_seq_gen",sequenceName = "groupmembers_seq")
private Long memberid;
// Other values
}
Edit :
This is how I retrieve the current user, but I cannot find how to check to which object it is, I can get an Object of UserDetails, but as both methods are implementing UserDetails, I cannot tell which one it is.
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication == null){
return null;
} else {
return personDAO.findPersonByUsername(authentication.getName());
}
}
I hope this should be simple.
You have two UserDetails objects User1 and User2, suppose User1 is of the Person class and User2 is of GroupPerson.
You can get theUserDetails Object as you stated, then all you need to do is to check if the object is instance of Person or GroupMembers.
You can do it using instanceof like the following
if(userObject instanceof Person){
// DO Stuff
}
else if(userObject instanceof GroupMembers){
// Do Stuff
}
Here your userObject can be an object of either Person or GroupMember

spring security using kerberos/spnego authentication

I have got spring security using kerberos authentication successfully working. But it seems the spring framework is invoking KerberosServiceAuthenticationProvider.userDetailsService to get the roles, I would have thought that it gets the roles only once until the session is invalidated. My config looks like
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http entry-point-ref="spnegoEntryPoint" auto-config="false">
<intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/j_spring_security_check*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
<form-login login-page="/login.html" default-target-url="/" always-use-default-target="true"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="kerberosServiceAuthenticationProvider" />
<authentication-provider ref="kerberosAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="spnegoEntryPoint"
class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />
<beans:bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
<beans:property name="failureHandler">
<beans:bean class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html" />
<beans:property name="allowSessionCreation" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
<beans:property name="ticketValidator">
<beans:bean
class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
<beans:property name="servicePrincipal" value="HTTP/mywebserver.corpza.corp.co.za"/>
<beans:property name="keyTabLocation" value="classpath:mywebserver.keytab" />
<beans:property name="debug" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean id="kerberosAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosAuthenticationProvider">
<beans:property name="kerberosClient">
<beans:bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosClient">
<beans:property name="debug" value="true" />
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean class="org.springframework.security.extensions.kerberos.GlobalSunJaasKerberosConfig">
<beans:property name="debug" value="true" />
<beans:property name="krbConfLocation" value="/etc/krb5.conf" />
</beans:bean>
<beans:bean id="dummyUserDetailsService" class="main.server.DummyUserDetailsService"/>
</beans:beans>
so my DummyUserDetailsService.loadUserByUsername(Styring username) is invoked each time a secure page is requested, I am loading the user roles from database and don't want to run the query each time a request is made, is there any configuration I need to do to prevent this?
thanks Michael, I got it working by extending SpnegoAuthenticationProcessingFilter class and overriding doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (skipIfAlreadyAuthenticated) {
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if (existingAuth != null && existingAuth.isAuthenticated()
&& (existingAuth instanceof AnonymousAuthenticationToken) == false) {
chain.doFilter(request, response);
return;
}
}
super.doFilter(req, res, chain);
}
Tell Spring Security to cache the authentication in the HTTP Session. Here is how.

Spring controller with #RequestBody and #ModelAttribute for the same parameter

I want to create a controller method that can be called from html form or from a standalone client using resttemplate.
The method has this signature:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView save(#RequestBody #ModelAttribute(value = "itemCreateRequest")
ItemCreateRequest request, Model model) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
...
}
The problem is, that if I use the HTML form, everything works fine, but when I try to use the standalone client the values in the request parameter come in null.
ItemCreateRequest icr = new ItemCreateRequest();
icr.setItem(new ItemDTO(null, "23", "bla", "bla bla"));
restTemplate.postForLocation("http://localhost:8080/ims_ui/items.xml", icr);
If I try the method without the #ModelAttribute the standalone client works but the HTML form not.
This is the configuration of my servlet-context.xml
<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<beans:bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<beans:property name="marshaller" ref="marshaller"/>
<beans:property name="unmarshaller" ref="marshaller"/>
</beans:bean>
<beans:bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<beans:bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<beans:property name="order" value="0" />
<beans:property name="mediaTypes">
<beans:map>
<beans:entry key="jsp" value="application/html" />
<beans:entry key="xml" value="application/xml" />
</beans:map>
</beans:property>
<beans:property name="defaultViews">
<beans:list>
<!-- XML View -->
<beans:bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<beans:constructor-arg ref="marshaller" />
</beans:bean>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<beans:property name="contextPaths">
<beans:array>
<beans:value>net.franciscovillegas.summa.ims.dto</beans:value>
<beans:value>net.franciscovillegas.summa.ims.service.requests</beans:value>
</beans:array>
</beans:property>
</beans:bean>
Thanks in advance!
The answer is in you're post:
If I try the method without the #ModelAttribute the standalone client
works but the HTML form not.
I think that, when you use the html application you send a request to the server with a map where all keys starts with itemCreateRequest.VARIABLE_NAME , but when you use stand alone application you send that request without the declared #ModelAttribute.
The declared ModelAttribute, in you case "itemCreateRequest", should stand in front of each information sent to that controller.
You can try something like this:
restTemplate.postForLocation("http://localhost:8080/ims_ui/items.xml", new HashMap<String, Object>(){
{
put("itemCreateRequest", icr);
}
});

Resources