spring security stay on single url at login - authentication

here is my case.
when first time user land to site, they will pointed to login page. (lest say http://ex.com/) and when they successfully login, they'll see the other page with the same url (http://ex.com/
but, when they open the site on other tab (http://ex.com) they will pointed back to login page.
how to implement this case in my site with spring security ?
its easy to do when deal with conventional servlet. i just need to have 2 method (doGet for showing login page, and doPost for authenticating user and if its valid it will call another view).
here is my configuration :
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/login"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>

** Edited (remove unrelated answer)
It appears you need to add a concurrent session management using Spring Security. See the following link: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
You can inject the SessionRegistry and see if the principal is already logged-in. If he is, call the expireNow()
Or you can implement a filter on or before SessionManagementFilter in the FilterChainProxy:
The SessionManagementFilter checks the contents of the SecurityContextRepository against the current contents of the SecurityContextHolder to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me [19]. If the repository contains a security context, the filter does nothing. If it doesn't, and the thread-local SecurityContext contains a (non-anonymous) Authentication object, the filter assumes they have been authenticated by a previous filter in the stack. It will then invoke the configured SessionAuthenticationStrategy.
- http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html

I think your configuration has a problem
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="<home-page-url. ex: /home>"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
The default-target-url should point to the default page to which the application has to redirect after a successful login.
EDITED
After going through the required posted again, I think the approach is to make the controller handling /login request to handle both cases
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class AppsController {
#RequestMapping("/login")
public ModelAndView view(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
User user = authentication != null
&& authentication.getPrincipal() instanceof User ? (User) authentication
.getPrincipal() : null;
return user == null ? getLoginModelAndView() : getHomeModelAndView();
}
private ModelAndView getHomeModelAndView() {
return null;
}
private ModelAndView getLoginModelAndView() {
return null;
}
}
If there is no authenticated user present in the session the controller will return the log-in page, but once the user is logged-in then it will return a different page.
Spring security will cache the logged used to the user session and it can be retrieved using the SecurityContextHolder.

Related

unauthorizedRedirect set to false, still redirecting

I'm writing some REST api for my cake 3.0 application, and I need to set $this->Auth->unauthorizedRedirect to false, as the manual says that this would prevent my application to redirect to login url for unauthorized requests.
http://api.cakephp.org/3.0/class-Cake.Auth.BasicAuthenticate.html
The problem is that I'm trying to set it in my Users controller, and it doesn't work:
class UsersController extends AppController {
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->Auth->allow(['logout']);
// Change the authentication mode when using REST api
if(! $this->RequestHandler->accepts('html')) {
$this->Auth->unauthorizedRedirect = false;
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
}
}
}
This scripts works fine as detecting if a user is actually registered, but fails when I try to use wrong authentication data, showing the login form instead of throwing an error. What am I doing wrong?
Authentication and authorization are two different things
You are mixing up authentication and authorization, that's two different things. Logging in a user is authentication, testing whether a logged in user is allowed to access a specific action is authorization.
So the unauthorized redirect configuration applies to logged in users when accessing actions.
Handling unauthenticated requests
What you are looking for, ie throw an exception on unauthenticated requests, is done by the basic authentication adapter by default, so I assume that you actually aren't using this adapter!?
So if you are using a different adapter, this behavior is best implemented in either your controller where you are trying to identify the user
$user = $this->Auth->identify();
if (!$user) {
throw new ForbiddenException('Stop! Hammer time!');
} else {
$this->Auth->setUser($user);
}
or, in case you want the exception to be thrown for every controller, in a custom authentication adapters unauthorized() method, which is being invoked on unauthenticated requests before executing possible redirects. Quote from the docs:
Cookbook > Authentication > Handling Unauthenticated Requests
When an unauthenticated user tries to access a protected page first the unauthenticated() method of the last authenticator in the chain is called. The authenticate object can handle sending response or redirection by returning a response object, to indicate no further action is necessary. Due to this, the order in which you specify the authentication provider in authenticate config matters.
If authenticator returns null, AuthComponent redirects user to login action. [...]
Here's a simple example that extends the form authentication handler:
src/Auth/MyCustomAuthenticate.php
namespace App\Auth;
use Cake\Auth\FormAuthenticate;
use Cake\Network\Exception\ForbiddenException;
use Cake\Network\Request;
use Cake\Network\Response;
class MyCustomAuthenticate extends FormAuthenticate
{
public function unauthenticated(Request $request, Response $response)
{
if(!$request->accepts('text/html')) {
throw new ForbiddenException('Ah ah ah! You didn\'t say the magic word!');
}
}
}
Controller
$this->loadComponent('Auth', [
'authenticate' => [
'MyCustom'
]
]);
See also
Cookbook > Authentication > Creating Custom Authentication Objects
Cookbook > Authentication > Using Custom Authentication Objects

Login via pre authentication filter or form login in spring security

I am looking for a way where you can have two ways to get access to my website.
1) You should be able to use form-login and get your authorities and use the website.
2) You should be able to login into another website and there you should be able to press a link with a token and be logged in to my website. (I control both websites and they use the same database)
Stage 1 is completed and works well and I have made stage 2 something similar to this https://stackoverflow.com/a/9919988/1915913 and that is working as well, I get a token and I am able to verify it and login.
But my problem is, how can I make them both work for me at the same time, for the same resources. I am pretty sure I know what the problem is, I create a custom filter and i try to use the form-login filter.
That does not work, but can it? Or is there some other way i can get this functionality?
This does not work in a way that the pre-auth filter seems to take over and i cant get the normal login to work and it seems to call the pre-auth filter everytime i go to a new page in the project.
The classes I use for this are all pretty simple.
My security-app-context:
<http pattern="/**" use-expressions="true" create-session="always">
<intercept-url pattern="/login.jsp*" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<custom-filter position="PRE_AUTH_FILTER" ref="PreAuthenticatedProcessingFilter" />
<form-login
username-parameter="idnumber"
password-parameter="password" login-processing-url="/processlogin"
login-page='/login.jsp'
authentication-failure-handler-ref="myAuthErrorHandler"
authentication-success-handler-ref="mySuccessHandler"
always-use-default-target='true'
authentication-failure-url="/login.jsp?login_error=true"/>
<logout logout-url="/logout/" logout-success-url="/login.jsp" delete-cookies="JSESSIONID"/>
<session-management invalid-session-url="/">
<concurrency-control expired-url="/" max-sessions="2" />
</session-management>
</http>
<!-- form login -->
<beans:bean id="mySuccessHandler" class="is.inna.rest.login.SuccessHandler"/>
<beans:bean id="myAuthErrorHandler" class="is.inna.rest.login.AuthentificationListener"/>
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<beans:bean name="myUserDetailsService" class="is.inna.rest.login.LoginUserDetailService" />
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<!-- Pre auth -->
<beans:bean id="userDetailsServiceWrapper" class="is.inna.rest.login.AuthUserDetailService" />
<beans:bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="userDetailsServiceWrapper"/>
</beans:bean>
<beans:bean id="PreAuthenticatedProcessingFilter" class="is.inna.rest.login.PreAuthenticatedProcessingFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
My user details services
public class AuthUserDetailService implements AuthenticationUserDetailsService<Authentication> {
#Override
public UserDetails loadUserDetails(Authentication authentication) throws UsernameNotFoundException {
String id = (String) authentication.getPrincipal();
NotandiHelper notandi = UserDAO.getNotandiByToken(id);
return new User(notandi.getUsername(), notandi.getPassword(), notandi.getAuthorities());
}
}
My pre auth filter
public class PreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
if(request.getParameter("id") != null){
return request.getParameter("id");
}else if(request.getParameter("idnumber") != null){
return request.getParameter("idnumber");
}
return null;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
if(request.getParameter("kt") != null){
String[] credentials = new String[2];
credentials[0] = request.getParameter("token");
credentials[2] = request.getParameter("id");
return credentials;
}
if(request.getParameter("idnumber")!= null){
String[] credentials = new String[2];
credentials[0] = request.getParameter("idnumber");
credentials[1] = request.getParameter("password");
return credentials;
}
return null;
}
When you're extending a class, you really need to understand how the base class works (one of the problems with inheritance). In this case, you are returning "false" in the case where there is no token present, which is an arbitrary choice for a method returning an object. If you look at the base class, you will see that it checks for a non-null principal and returns immediately from doAuthenticate if the value is null, causing the filter chain to proceed immediately, as if the pre-authentication filter wasn't there at all. So that's not right. Try returning null instead.
Note that the debug log should also contain a log message saying
preAuthenticatedPrincipal = null, trying to authenticate
It's also unclear from your configuration how you authenticate the token. There doesn't seem to be anything there to do that.
Note that you'll need to use the entry point for form-login if you want the login form to be displayed automatically for unauthenticated users. The http403EntryPoint will just return immediately with a 403 code.

how to obtain a list of all currently logged-in users (including rememberme cookies) in grails with spring security

I'm building a grails app that has the spring-security-core 1.2.7.3 plugin as well as spring-security-ui 0.2 plugin, and would like to obtain a list of ALL the users that are currently logged in (ie have a currently active session). Users can login either through a login controller (daoAuthenticationProvider) or automatically through a rememberMe cookie.
I have implemented the code below, using ConcurrentSessionControlStrategy to create a sessionRegistry:
in /conf/spring/resources.groovy:
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
beans = {
userDetailsService(lablore.MyUserDetailsService)
sessionRegistry(SessionRegistryImpl)
sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
maximumSessions = -1
}
concurrentSessionFilter(ConcurrentSessionFilter){
sessionRegistry = sessionRegistry
expiredUrl = '/login/concurrentSession'
}
}
In /plugins/spring-security-core/conf/DefaultSecurityConfig.groovy
useHttpSessionEventPublisher = true
In the controller:
controller{
def sessionRegistry
action(){
def loggedInUsers = sessionRegistry.getAllPrincipals()
}
}
It works well for
-users that login through the login page
-users that logout through a 'logout' link
-users who's session expires
HOWEVER, it does NOT work for users that authenticate automatically with a rememberMe cookie. It doesn't see that they have a newly created session.
If I understand correctly, this is because the RememberMeAuthenticationFilter is 'further up' in the filter chain compared to the ConcurrentSessionFilter, which is the one running the sessionRegistry? Or, I messed something up with my configurations....
Any help on how to get this to work would be great !
Thanks!!
The ConcurrentSessionControlStrategy is deprecated,
Use the ConcurrentSessionControlAuthenticationStrategy instead
Alternatively,
You can implement the HttpSessionListener interface which has the sessionCreated(HttpSessionEvent event) and sessionDestroyed(HttpSessionEvent event) methods, But you have to add the class you used
Implementations of this interface are notified of changes to the list of active sessions in a web application. To receive notification events, the implementation class must be configured in the deployment descriptor for the web application.
You can either add the implementation class to your deployment descriptor like so(i.e you web.xml file)
<listener>
<listener-class>com.hazelcast.web.SessionListener</listener-class>
</listener>
or by using the WebXmlConfig plugin in grails
Your implementation class could look like below, see Online users with Spring Security also
class WebSessionListener implements HttpSessionListener{
sessionCreated(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
sessionDestroyed(HttpSessionEvent se){
//Checked if user has logged in Here and keep record
HttpSession webSession = se.getSession();
}
}

Spring Security: forward to original page after login with admin user after access was denied

I use Spring security to authenticate users. If a user requests a secured page, he has to authenticate over a login page. If the user is always authenticated, he will be redirected to the requested page immediatly. Moreover some pages need special access rights, and so I setup an access-denied-page temporarily. So far so good.
The scenario:
The scenario definies, that the user will get a login-form instead of a static access-denied page, so that a different user can authenticate and if authentication is successful the requested page that needs the higher privileges will open.
The actual spring configuration reads:
<security:http auto-config="true" use-expressions="true" disable-url-rewriting="true">
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/loginView" access="permitAll" />
<security:intercept-url pattern="/accessDenied" access="permitAll"/>
<security:intercept-url pattern="/user" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/**" access="denyAll"/>
<security:form-login login-page="/loginView"
authentication-failure-url="/loginView"
default-target-url="/dirView" />
<security:logout />
<security:access-denied-handler ref="accessDeniedHandler" />
</security:http>
The accessDeniedHandler-Bean:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
/** {#inheritDoc} */
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
But that implementation of AccessDeniedHandler only forwards to the loginView. After authentication of an admin the default-success-page is openend and not the original requested page. I also tried to save the original request by calling HttpServletRequest#getAttribute("javax.servlet.forward.servlet_path"), but I don't understand how to force spring security to use that original request instead of the default target url.
Besides I read about org.springframework.security.web.savedrequest.SavedRequest that is used inside spring authentication to remember the original request if an unauthenticated user requests a page. But I don't find a valid way how to use the SavedRequest in the same manner for my access denied scenario.
Thanks in advance for suggestions and solutions.
I think your requirements should be satisfied by using the RequestCache API.
If you modify your http configuration you can use the request-cache namespace element:
<security:http>
...
<security:request-cache ref="requestCache"
</security:http>
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache" />
You can also inject it into your AccessDeniedHandler. Then all you should need is a simple to saveRequest to setup the cached request which should be restored post-authentication:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
// Inject this into your class.
private RequestCache requestCache;
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
requestCache.saveRequest(req, resp);
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
Strictly speaking, you don't actually need to do the bit with the namespace at all, since HttpSessionRequestCache is stateless (it's the internal implementation which is used if you don't override it in the namespace). So you could just create one directly in your AccessDeniedHandler class and it would still work.

How does Seam 3 handles the "redirect to capture view" feature after login?

Here is my use cases.
I have a login page which is /public/login.xhtml. All my other pages are required to log-in before reaching them. They are in /pages/ directory.
I want that :
If my user access to http://host/myapp/pages/* it redirects him first to the login page, and then, to the URL he has firstly entered.
If my user access to http://host/myapp/, it redirects him first to the login page, and then, to /pages/home.xhtml.
If my user access to http://host/myapp/public/login.xhtml, it redirects him first to the login page, and then, to /pages/home.xhtml.
If my user access to http://host/myapp/public/login.xhtml and is already logged in, it redirects to /pages/home.xhtml.
What is working currently?
With Seam 3 (v3.1.0.Final) and the Security + Faces module, my use case n°1 is automagically working with :
#ViewConfig
public interface PagesConfig {
static enum Pages {
#ViewPattern("/pages/*")
#LoginView("/public/login.xhtml")
#LoggedIn
LOGGED_IN_PAGES,
}
}
My problem is that I don't understand how Seam's working to do that redirection to the "capture view".
With Seam 2, it was easy to understand, in components.xml we had
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}" />
</event>
<event type="org.jboss.seam.security.loginSuccessful">
<action execute="#{redirect.returnToCapturedView}" />
</event>
So we captured the events notLoggedIn and loginSuccessful to handle that with a redirect component.
In Seam 3, I didn't found that configuration : nothing seems to #Observes LoggedInEvent, and there is no Redirect class...
The point n°2 is achieved with that /index.htm file :
<html><head>
<meta http-equiv="Refresh" content="0; URL=pages/home.xhtml">
</head></html>
But for my point n°3, I've tried solutions which don't fully work.
First I tried that in login.xhtml :
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" if="#{identity.loggedIn}" immediate="true" />
</f:metadata>
And with or without onPostback="true", after I login, I'm still in the login page with that error message (twice) : "Unable to find matching navigation case with from-view-id «/public/login.xhtml» for action «#{identity.login}» with outcome «success».". It's only if I now re-access to http://host/myapp/public/login.xhtml that my viewAction redirects me to the home.
I also tried that navigation-rule in faces-config.xml :
<navigation-rule>
<from-view-id>/public/login.xhtml</from-view-id>
<navigation-case>
<if>#{identity.loggedIn}</if>
<to-view-id>/pages/home.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
But then, my use case n°1 was disabled : every time I logged-in, I was redirected to the home.
Finally, for my point n°4, the s:viewAction does the job.
So does somebody knows the best practices in order to correctly handle those 4 use cases (which I think are common use cases), especially the point n°3?
Use case No. - 1 SeamFaces stores the originally requested viewId in the user Session, then re-routes to that view after the successful login. It does this by intercepting the navigation from the Seam Security login button, and fires a PostLoginEvent with the data stored in the SessionMap.
Use case No. 2 - nice solution with the redirect! You could also do this with a #UrlMapping in your ViewConfig.
Use case No. 3 - Your viewAction solution should work, but I believe you are coming across SEAMFACES-179. There are a couple of solutions you can use:
1) In your login method, you can manipulate the seesion map stored by the Seam Faces, as demonstrated in this gist -- (this solution courtesy of Cody Lerum)
2) Use PrettyFaces to intercept the request for the login view, and rediret you if you are not logged in.
Finally here is what I did.
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" immediate="true" />
</f:metadata>
So I removed the if="#{identity.loggedIn}" in order to call my redirectToHome method which redirects to
the /pages/home.xhtml.
If the user is already authenticated, then he is redirected to the home page.
If he's not, then it is redirected to the home page, which redirects him to the login page thanks to my #ViewConfig
Here is the loginAction :
public void redirectToHome() throws IOException {
externalContext.redirect(externalContext.encodeActionURL(externalContext.getRequestContextPath()+"/pages/home.xhtml"));
}
The problem I faced then was when I logged out.
Here is my logout action :
<h:commandLink action="/public/login" actionListener="#{securityAction.logout()}" value="Disconnect" immediate="true" />
And the securityAction.logout() method :
public void logout() {
identity.logout();
if (!conversation.isTransient()) {
conversation.end();
}
}
The problem is that I was redirected to the login page (thanks to the #ViewConfig I think), but no PreLoginEvent were thrown, so the Seam LoginListener.observePreLoginEvent wasn't called, and so my previous URL wasn't put in session. So when I logged in (immediatly after logout), I was stuck on the login page, but was logged in.
Thanks to Brian Leathem and he's previous answer, here is what I did : in my authenticate method of my BaseAuthenticator, I called that method after authentication :
private void overrideRedirectToLogin() {
final String PRE_LOGIN_URL = LoginListener.class.getName() + "_PRE_LOGIN_URL";
final ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
final Map<String, Object> sessionMap = externalContext.getSessionMap();
String redirectURL = (String) sessionMap.get(PRE_LOGIN_URL);
if (redirectURL == null) {
final HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
redirectURL = request.getRequestURL().toString();
}
sessionMap.put(PRE_LOGIN_URL, redirectURL.replace("/public/login.xhtml", "/pages/home.xhtml"));
}
With that solution, my previous URL wasn't set in session, but at least, my user is redirected to the home page.