Spring MVC - Checking if User is already logged in via Spring Security?
I have a Spring MVC application.It uses its own custom Login page. Upon successful login, a 'LOGGED_IN_USER' object is placed in the HTTPSession.
I want to allow only authenticated users to access URLs. I know i can achieve this by using a web filter. But, This part i want to do using Spring Security (my check will remain the same - look for 'LOGGED_IN_USER' object in HTTPSession, if present you are logged in).
My constraint is i cannot change Login behavior at present - that will not use Spring Security yet.
What aspect of Spring Security can i use to achieve this part alone - check if the request is authenticated (from logged in user)?
There are at least 4 different ways:
spring security XML configuration
this is the easiest way
<security:http auto-config="true" use-expressions="true" ...>
...
<security:intercept-url pattern="/forAll/**" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
- @see Spring Security Reference, Chapter 16.1.1 Common Built-In Expressions
- @see Spring Security Reference, Chapter 16.2 Web Security Expressions
Per @Secured Annotation
requires <global-method-security secured-annotations="enabled" />
@Secured("ROLE_ADMIN")
@RequestMapping(params = "onlyForAdmins")
public ModelAndView onlyForAdmins() {
....
}
Per @PreAuthorize Annotation
requires <global-method-security pre-post-annotations="enabled" />
@PreAuthorize("isAuthenticated()")
@RequestMapping(params = "onlyForAuthenticated")
public ModelAndView onlyForAuthenticatedUsers() {
....
}
Programmatic
SecurityContextHolder.getContext().getAuthentication() != null &&
SecurityContextHolder.getContext().getAuthentication().isAuthenticated() &&
//when Anonymous Authentication is enabled
!(SecurityContextHolder.getContext().getAuthentication()
instanceof AnonymousAuthenticationToken)
Custom Expression
If the built-in expressions are not enough, you can extend them. How to extend the SpEL Expressions for the method annotations is discussed for example here:
- How to create custom methods for use in spring security expression language annotations
- http://bmchild.blogspot.de/2012/02/creating-custom-regex-spring-security.html
But for the interceptor <security:intercept-url ... access="myCustomAuthenticatedExpression" />
there is a slightly different approach possible, that does not need to deal with the private class problem. -- I have only done it for Spring Security 3.0, but I hope it works for 3.1 too.
1.) you need to create a new class that extends from WebSecurityExpressionRoot
(Prefix Web is the important part!).
public class MyCustomWebSecurityExpressionRoot
extends WebSecurityExpressionRoot {
public MyCustomWebSecurityExpressionRoot(Authentication a,
FilterInvocation f) {
super(a, f);
}
/** That method is the one that does the expression evaluation! */
public boolean myCustomAuthenticatedExpression() {
return super.request.getSession().getValue("myFlag") != null;
}
}
2.) you need a extend the DefaultWebSecurityExpressionRootHandler
to have a handler that provides your custom expression root
public class MyCustomWebSecurityExpressionHandler
extends DefaultWebSecurityExpressionHandler {
@Override
public EvaluationContext createEvaluationContext(Authentication a,
FilterInvocation f) {
StandardEvaluationContext ctx =
(StandardEvaluationContext) super.createEvaluationContext(a, f);
WebSecurityExpressionRoot myRoot =
new MyCustomWebSecurityExpressionRoot(a, f);
ctx.setRootObject(myRoot);
return ctx;
}
}
3.) Then you need to register your handler with the voters
<security:http use-expressions="true"
access-decision-manager-ref="httpAccessDecisionManager" ...>
...
<security:intercept-url pattern="/restricted/**"
access="myCustomAuthenticatedExpression" />
...
</security:http>
<bean id="httpAccessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<ref bean="webExpressionVoter" />
</list>
</constructor-arg>
</bean>
<bean id="webExpressionVoter"
class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler"
ref="myCustomWebSecurityExpressionHandler" />
</bean>
<bean id="myCustomWebSecurityExpressionHandler"
class="MyCustomWebSecurityExpressionHandler" />
Spring Security 3.1 Update
Since Spring Security 3.1 it is a bit easier to implement a custom expression. One does not longer need to sublcass WebSecurityExpressionHandler
and override createEvaluationContext
. Instead one sublass AbstractSecurityExpressionHandler<FilterInvocation>
or its subclass DefaultWebSecurityExpressionHandler
and override SecurityExpressionOperations createSecurityExpressionRoot(final Authentication a, final FilterInvocation f)
.
public class MyCustomWebSecurityExpressionHandler
extends DefaultWebSecurityExpressionHandler {
@Override
public SecurityExpressionOperations createSecurityExpressionRoot(
Authentication a,
FilterInvocation f) {
WebSecurityExpressionRoot myRoot =
new MyCustomWebSecurityExpressionRoot(a, f);
myRoot.setPermissionEvaluator(getPermissionEvaluator());
myRoot.setTrustResolver(this.trustResolver);
myRoot.setRoleHierarchy(getRoleHierarchy());
return myRoot;
}
}
Another solution, you can create class:
public class AuthenticationSystem {
public static boolean isLogged() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return null != authentication && !("anonymousUser").equals(authentication.getName());
}
// ...
// Any another methods, for example, logout
}
Then, in controller:
@Controller
@RequestMapping(value = "/promotion")
public final class PromotionController {
@RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public final String root() {
if (!AuthenticationSystem.isLogged()) return "login"; // or some logic
// some logic
return "promotion/index";
}
}
PS:
Previous solution has a problem, which explain Peter in comments.
@Controller
@RequestMapping(value = "/promotion")
public final class PromotionController {
@RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public final String root(final Principal principal) {
if (null == principal) return "login"; // or some logic
// some logic
return "promotion/index";
}
}
Is this what you're trying to achieve?
<c:choose>
<c:when test="${pageContext.request.userPrincipal.authenticated}">Show something</c:when>
<c:otherwise>Show something else</c:otherwise>
</c:choose>
Many of the authentication providers will create a UserDetails object as the principal.
Another way I found - using spring-security - is to check whether the return value of Authentication.getPrincipal()
is an instance of UserDetails
; the method returns "anonymousUser"
(String
) by default.
boolean isUserLoggedIn(){
return SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserDetails
}