Spring Security - Redirect if already logged in - spring

I'm new to Spring:
I do not want authenticated user from accessing the login page. What is the proper way to handle redirects for the '/login' if the user is already authenticated? Say, I want to redirect to '/index' if already logged in.
I have tried 'isAnonomous()' on login, but it redirects to access denied page.
<security:http auto-config="true" use-expressions="true" ...>
<form-login login-processing-url="/resources/j_spring_security_check"
default-target-url="/index"
login-page="/login" authentication-failure-url="/login?login_error=t" />
<logout logout-url="/resources/j_spring_security_logout" />
...
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

In the controller function of your login page:
check if a user is logged in.
then forward/redirect him to the index page in that case.
Relevant code:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
/* The user is logged in :) */
return new ModelAndView("forward:/index");
}
Update
Or in another scenario where the mapping may be containing path variable like #GetMapping(path = "/user/{id}") in this case you can implement this logic as well:
#GetMapping(value = "/login")
public String getLogin() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
User loggedInUser = userService.findByEmail(auth.getName())
.orElseThrow(Exception::new);
/* The user is logged in :) */
return "redirect:/user/" + loggedInUser.getUserId();
}
return "login";
}

To successfully redirect from login page, if user is already logged in, add the following to your login.jsp:
Add a security taglib header to the top of your jsp:
<%#taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
Then add the following tag inside your "head" tag (preferably near the top):
<sec:authorize access="isAuthenticated()">
<% response.sendRedirect("main"); %>
</sec:authorize>
This will redirect to main.html (or whatever your main .jsp is mapped to) if the user accessing the login page is already logged-in.
Doing this through a controller didn't work for me, since the valid login page practice is to let the spring security's "form-login" bean do all the redirecting work, so there was no login controller for me to modify.

login.xhtml
<h:head >
<f:metadata>
<f:event type="preRenderView" listener="#{loginBean.onPageLoad}"/>
</f:metadata>
</h:head>
loginBean
public void onPageLoad(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
try {
FacesContext.getCurrentInstance().getExternalContext().redirect(url);
} catch (IOException e) {
e.printStackTrace();
}
}
}

hey you can do that.
<h:head>
<sec:authorize access="isAuthenticated()">
<meta http-equiv="refresh" content="0;url=http://your index.xhtml url (full url)" />
</sec:authorize>
</h:head>
This method is very simple and convenient, is not it?

Related

Spring Security 3.2 - Adding Signup Form to Login Page

I have integrated Spring Security login successfuly to my wb application. However, I would like to have in the login page, in addition to login form, a signup form.
In order to do validation I need to pass to that form the object SignupForm which represents my form.
How can I do it? I have tried so many approaches and nothing works.
Please help....
ApplicationContext.xml
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list><value>classpath:com/harryfwong/config/messages</value></list>
</property>
</bean>
message.properties in com.harryfwong.config resource package
username.required=Username is required
password.required=Password is required
passwordconfirm.mustmatch=Password must match
email.required=Email is required
Spring Validator
<!-- language: java -->
public class SignUpFormValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return SignUpForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required");
SignUpForm form = (SignUpForm) target;
if (!StringUtils.isBlank(form.getPassword())) {
if (!form.getPassword().equals(form.getPasswordConfirm())) {
errors.rejectValue("passwordConfirm", "passwordconfirm.mustmatch");
}
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "email.required");
}
}
Controller
<!-- language: java -->
#RequestMapping(method=RequestMethod.GET, value="signup")
public String signup(#ModelAttribute("form") SignUpForm form) {
if (form == null) {
form = new SignUpForm();
}
return "auth/signup";
}
#RequestMapping(method=RequestMethod.POST, value="signup")
public String signupSuccess(#ModelAttribute("form") SignUpForm form, BindingResult result, HttpServletRequest request) {
SignUpFormValidator suValidator = new SignUpFormValidator();
suValidator.validate(form, result);
if (result.hasErrors()) {
return signup(form);
} else {
authenticateUserAndSetSession(form, request);
return "auth/signupSuccess";
}
}
External Reference Login after signup: Auto login after successful registration
What does your SS configuration currently look like?
<form-login
login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/landing"
always-use-default-target="false" />
Where /login is the request mapping
#RequestMapping(value="login")
public String login() {
// do something prior to display form below
return "auth/login";
}
auth/login.jsp
<form name='f' action="<c:url value='/loginProcess' />"
method='POST'>
Not sure what your security context config looks like - this is a response to your issue about not getting to the signup page
<http use-expressions="true">
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/loggedout" access="permitAll"/>
<intercept-url pattern="/signup" access="permitAll"/>
<intercept-url pattern="/" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
...
</http>

logout via Spring 3 Security

No matter what I do I cannot get logout to work. I have a menu that is only visable via members role. I have tried both invalidate of the session, as well as the /j_spring_security_logout via the Spring documentation... Both forward to the home page, but the menu and credentials appear on the home page. Thus the logout does not work. Here is my Spring Security.xml file entries.
<logout logout-url="/logout"/>
<logout logout-url="/j_spring_security_logout" logout-success-url="/home"
delete-cookies="JSESSIONID" invalidate-session="true"/>
As you can see I have tried both. Here is the logout controller.
#RequestMapping(value = "/logout", method=RequestMethod.GET)
public String logout(HttpServletRequest request, HttpServletResponse response) {
logger.info("Logging out!");
response.setHeader("pragma", "no-cache");
response.setHeader("Cache-control", "no-cache, no-store, must-revalidate");
response.setHeader("Expires", "0");
Cookie jSessionCookie = new Cookie("JSESSIONID", null);
jSessionCookie.setMaxAge(0);
response.addCookie(jSessionCookie);
Cookie activityIdCookie = new Cookie("activityId", null);
activityIdCookie.setMaxAge(0);
response.addCookie(activityIdCookie);
request.getSession().invalidate(); //Attempt to logout user.
return ("redirect://");
}
Any help is greatly appreciated.
You only need one logout handler and NO additional controller:
<logout logout-url="/logout" logout-success-url="/home" />
Just let the user request the url http://localhost/yourApp/logout.

GWT + Spring Security login issue after logout

I'm using Spring Security to secure access to my GWT's first page application "/HumanResources.html". It checks if the credentials of the user are correct (by comparing them with ldap content) and look if the user exists in database (custom authorized table).
First time user login, there is no trouble, then the user logout and a ".jsp" page is displayed.
But when he wants to access again to "HumanResources.html" page, the authentication is apparently ignored (login form isn't displayed) and the page is displayed. Only the interface is visible, data is retrieved by secured RPC services.
This problem appears on external Tomcat Server (tested on Firefox and Chrome), but not on GWT Dev mode. CTRL + F5 seems to work, and I looked for other cache issues but it didn't help.
Can anyone help me ?
A part of my security-applicationContext.xml :
<http use-expressions="true" auto-config="false">
<intercept-url pattern="/HumanResources.html" access="isAuthenticated()" />
<form-login
login-page='/login.jsp'
authentication-failure-url = "/login.jsp?login_error=1"
authentication-success-handler-ref="HRAuthenticationHandler" />
<logout
logout-url="/logout"
logout-success-url="/logout.jsp"
delete-cookies="JSESSIONID"/>
</http>
<beans:bean id="HRAuthenticationHandler" class="lu.sfeir.candidate.server.auth.HRAuthenticationHandler">
<beans:property name="useReferer" value="true" />
</beans:bean>
<ldap-server url="${ldap.serverUrl}" manager-dn="${ldap.adminLogin}" manager-password="${ldap.adminPassword}" />
<authentication-manager>
<ldap-authentication-provider
group-search-base="${ldap.groups}"
user-search-base="${ldap.users}"
user-search-filter="${ldap.userId}">
</ldap-authentication-provider>
</authentication-manager>
and my custom AuthenticationHandler implementation :
public class HRAuthenticationHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
private AuthorizedUsersDao usersDao;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
// Check if the user exist in the DB
if(usersDao.findUser(((UserDetails)authentication.getPrincipal()).getUsername())) {
// Redirect to home page
super.onAuthenticationSuccess(request, response, authentication);
} else {
// Redirect to error page
response.sendRedirect("/spring_security_login?login_error");
}
}
}
Edit:
Sorry for missleading You.
I dig a bit and found that security doesnt work cause user browser is caching xxx.html page, js,css etc
You can:
1) force refresh page by setting html headders :
Thats the simplest solution, and since You are using secure RPC services that should be enaught for You.
Or disable caching at all.
Mind it will increase Your network traffic.
2)
our application is checking if security error ocured in RPC:
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.InvocationException;
public abstract class CustomAsyncCallback<T> implements AsyncCallback<T> {
#Override
public void onFailure(Throwable caught) {
if (caught instanceof InvocationException) {
InvocationException ie = (InvocationException) caught;
if(ie.getMessage().contains("j_spring_security_check")) {
Window.alert("User session expired, please login again");
Window.open(GWT.getHostPageBaseURL() + "login.jsp", "_self", null);
return;
}
}
}
}
You must call it somewhere ie while displaying You current logged user:
userSecurityService.getUser(new CustomAsyncCallback<String>() {
#Override
public void onSuccess(String result) {
usrLoginLbl.setText(result);
}
});
or You may catch this exception to one of yours onFailure method

Getting logged in users with sessionRegistry not work when manually authenticate

I use spring security3 and spring mvc3 to build an web project. There is page called index.jsp, login user name and online user count will be displayed on
the top of this screen. There are 2 ways to login the system:
from login page, use default configuration post by 'j_spring_security_check'
ajax login with manually authentication
When I use login page to login into index page, both of count of online information and user name show correctly.
But when I use ajax login (manually authenticate), problem occurs: count of online user don't updated, it always displaying 0 while user name can show properly.
Part of the controller:
#Autowired
#Qualifier("authenticationManager")
AuthenticationManager authenticationManager;
#Autowired
SecurityContextRepository repository;
#RequestMapping(value="/ajaxLogin")
#ResponseBody
public String performLogin(
#RequestParam("j_username") String username,
#RequestParam("j_password") String password,
HttpServletRequest request, HttpServletResponse response) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
try {
Authentication auth = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
repository.saveContext(SecurityContextHolder.getContext(), request, response);
logger.info("Authentication successfully! ");
return "{\"status\": true}";
} catch (BadCredentialsException ex) {
return "{\"status\": false, \"error\": \"Bad Credentials\"}";
}
}
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-3.0.3.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/index" access="permitAll" />
<form-login login-page="/login" default-target-url="/index"
authentication-failure-url="/loginfailed" />
<logout logout-success-url="/logout" />
<session-management invalid-session-url="/index">
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
select login_id,login_pwd, is_enabled
from t_user where login_id=?"
authorities-by-username-query="
select u.login_id, r.authority from t_user u, t_roles r
where u.u_id = r.u_id and u.login_id =? " />
</authentication-provider>
</authentication-manager>
Method I used to get online login user count:
public class BaseController {
protected Logger logger = Logger.getLogger(this.getClass());
#Autowired
SessionRegistry sessionRegistry;
#ModelAttribute("numUsers")
public int getNumberOfUsers() {
logger.info("in getNumberOfUsers() ...");
return sessionRegistry.getAllPrincipals().size();
}
}
Code used to show login user name:
<div>
<security:authorize ifAllGranted="ROLE_USER">
<p>Welcome <security:authentication property="principal.username" />!
Logout</p>
</security:authorize>
</div>
code used to show count of logged in users:
<div style="color:#3CC457">
${numUsers} user(s) are logged in!
</div>
I guess that because when I manually authenticate, spring security not create new session for the user. I validate it by write customized SessionCounterListener.
public class SessionCounterListener implements HttpSessionListener {
private Logger logger = Logger.getLogger(this.getClass());
private static int totalActiveSessions;
public static int getTotalActiveSession(){
return totalActiveSessions;
}
#Override
public void sessionCreated(HttpSessionEvent event) {
totalActiveSessions++;
logger.info("sessionCreated - add one session into counter" + event.getSession().getId());
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
totalActiveSessions--;
logger.info("sessionDestroyed - deduct one session from counter" + event.getSession().getId());
}
}
Below is key content of log file for the action sequence: normal login -> normal logout -> ajax login -> ajax logout.
sessionDestroyed - deduct one session 1spueddcmdao019udc43k3uumw
sessionCreated - add one session 14nro6bzyjy0x1jtvnqjx31v1
sessionDestroyed - deduct one session 14nro6bzyjy0x1jtvnqjx31v1
sessionCreated - add one session e6jqz5qy6412118iph66xvaa1
Actually, ajax login/logout not give any output.
So now, how can I get correct login user count? And why the different authenticate ways has different method to deal with session? Any help will be appreciated.
As you are manually adding Principal to SecurityContext, it will not add user to SessionRegistry. You need to add user session to SessionRegistry manually.
SecurityContextHolder.getContext().setAuthentication(auth);
sessionRegistry.registerNewSession(request.getSession().getId(), auth.getPrincipal());
Hope it helps!!
In your Spring spring-security.xml file, the URL for the AJAX authentication (/ajaxLogin) is not explicitly allowed. Thus the request should be blocked by Spring. I would suggest to add this:
<intercept-url pattern="/ajaxLogin" access="permitAll" />

How to obtain a referrer URL with Spring MVC 3.1

I am building a website that has 2 pages. A recipe list page, a recipe detail page, and a sign in page. A user can sign in to the website by clicking on a sign in button on the recipe list page or recipe detail page. When a user click the sign in button, the user will be brought to the sign in page. I would like to redirect the user back again to the recipe detail page if they click the sign in button from the recipe detail page, or to the recipe list page if they click the sign in button from the recipe list page.
I wrote this method in a Controller class. This method will be called whenever user sign in to the website. I stored the referer URL into the session. The intention of saving this URL into a session is to keep track the page where user click on the sign in button. And also to redirect user to that page in the authentication handler that I wrote.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String openLoginPage(Model uiModel, HttpServletRequest request) {
String referrer = request.getHeader("Referer");
request.getSession().setAttribute("url_prior_login", referrer);
return RECIPE_LOGIN_PAGE;
}
I also created an authentication handler class called SuccessHandler.
public class SuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
/*
* (non-Javadoc)
*
* #see org.springframework.security.web.authentication.
* SavedRequestAwareAuthenticationSuccessHandler
* #onAuthenticationSuccess(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse,
* org.springframework.security.core.Authentication)
*/
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
String url = (String) request.getSession().getAttribute("url_prior_login");
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal != null && principal instanceof RecipeUser) {
request.getSession().setAttribute("RecipeUser", (RecipeUser) principal);
}
getRedirectStrategy().sendRedirect(request, response, url);
}
}
This class will redirect user to either the recipe list page or the recipe detail page when they sign in to the website. I register this class inside a security-context.xml file:
<http use-expressions="true" auto-config="false" entry-point-ref="authenticationEntryPoint">
<intercept-url pattern="/login" access="permitAll" />
<form-login login-page="/login" authentication-failure-url="/loginfail"
default-target-url="/login"
authentication-success-handler-ref="successHandler" />
<logout logout-success-url="/" />
</http>
<authentication-manager alias="authManager">
<authentication-provider user-service-ref='myUserDetailsService' />
</authentication-manager>
<beans:bean id="myUserDetailsService" class="com.safe.stack.service.security.UserService">
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<beans:bean id="successHandler"
class="com.safe.stack.service.security.SuccessHandler" />
<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login"/>
</beans:bean>
Is this a good way to do what I want to achieve ? Is there a better way to do this ? I could not find any example on how to this using Spring MVC.
Thank you
This is how I do it in Spring 3.1.4
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
// Supplies the default target Url that will be used if
// no saved request is found in the session
setDefaultTargetUrl("/member/dashboard");
super.onAuthenticationSuccess(request, response, authentication);
}
Extending SavedRequestAwareAuthenticationSuccessHandler is correct since the redirection is done automatically.
Just add new attribute "always-use-default-target" to "form-login" tag and set it to "false" (by default it is set to "true", even if don't specify it) like this:
<form-login
login-page="/login" authentication-failure-url="/loginfail"
default-target-url="/login"
authentication-success-handler-ref="successHandler"
always-use-default-target="false"
/>
You can read more about it in Spring Security documentation here, on end of section: 3.2.3.
If you add this, you won't need custom onAuthenticationSuccess anymore and you won't need to store anything in session on login page.

Resources