Spring MVC - Checking if User is already logged in via Spring Security?
Asked Answered
A

6

50

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)?

Aldine answered 11/9, 2012 at 14:14 Comment(0)
P
128

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>

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:

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;
      }
 }
Phosphorus answered 11/9, 2012 at 14:54 Comment(9)
If we do this: <security:intercept-url pattern="/**" access="isAuthenticated()" /> Where do i implement the isAuthenticated() method - i will have to provide the implementation for this since i am handling the sessions completely (not spring security)?Aldine
@Jasper: I have attached two links to spring security reference that list that already build in functions.Phosphorus
I cannot use the built-in isAuthenticated() function because Spring Security has no way of telling that user is already logged-in as: Login has been implemented in a custom fashion(not via Spring Security) - therefore it is impossible for Spring Security to tell if a user is logged-in. What happens is - Upon successful login a flag is put in Session Object indicating that user is logged-in. i need to check for the presence of that flag in Session object in my custom isAuthenticated() method - how/where can i do that?Aldine
@Jasper: sorry but I was (and I am still) not able to recognize this requirement from your question.Phosphorus
@Jasper: I have added a description how to build you own expression. That should help youPhosphorus
Thanks @Raplh for detailed explanation, i guess writing own Expressions will do the job.Aldine
spelling mistake: SecurityContextHilder => SecurityContextHolderThill
Im not sure if the Programmatic version works if you enabled anonymous authenticationUnsaddle
@jpaganz18, correct, one has also to check for !(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken) - I added this to my answer - thanksPhosphorus
B
19

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";
    }
}
Belovo answered 13/8, 2015 at 22:21 Comment(3)
This doesn't work because the principal is "anonymousUser" when the user is not authenticatedPearl
@peter yes, you are right, my bad, I updated solution.Belovo
Check anonymous authentication via import org.springframework.security.authentication.AnonymousAuthenticationToken; and authentication instanceof AnonymousAuthenticationTokenSubotica
B
11

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>
Barbellate answered 5/2, 2013 at 3:35 Comment(1)
Thanks nice one, but it does nt works on JSF app.. should it be different syntax ?Kareem
P
3

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
}
Pearl answered 31/12, 2018 at 10:47 Comment(0)
S
0

You can simply create a class that extends WebSecurityConfigurerAdapter and add authenticated in matching URL's

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/User/**").authenticated()
 }

you are good to go

Summitry answered 27/12, 2020 at 14:10 Comment(0)
V
0

I made an endpoint that looks like this:

    @GetMapping("/api/authorize")
    public boolean isUserLoggedIn() {
        boolean isLoggedIn = false;
        try {
            SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            isLoggedIn = true;
        } catch (Exception e) {
            isLoggedIn = false;
        }
        return isLoggedIn;
    }
Vaclava answered 14/2, 2022 at 19:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.