49

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

Jasper
  • 8,440
  • 31
  • 92
  • 133

6 Answers6

127

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;
      }
 }
Community
  • 1
  • 1
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • If we do this: **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)? – Jasper Sep 12 '12 at 04:15
  • @Jasper: I have attached two links to spring security reference that list that already build in functions. – Ralph Sep 12 '12 at 06:04
  • 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?** – Jasper Sep 12 '12 at 06:21
  • @Jasper: sorry but I was (and I am still) not able to recognize this requirement from your question. – Ralph Sep 12 '12 at 06:43
  • @Jasper: I have added a description how to build you own expression. That should help you – Ralph Sep 12 '12 at 07:21
  • Thanks @Raplh for detailed explanation, i guess writing own Expressions will do the job. – Jasper Sep 14 '12 at 10:15
  • spelling mistake: SecurityContextHilder => SecurityContextHolder – imxylz Nov 19 '13 at 09:45
  • Im not sure if the Programmatic version works if you enabled anonymous authentication – jpganz18 Mar 09 '16 at 21:22
  • @jpaganz18, correct, one has also to check for `!(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken)` - I added this to my answer - thanks – Ralph Mar 10 '16 at 05:58
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";
    }
}
Alexey Nikitenko
  • 2,047
  • 2
  • 20
  • 30
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>
Christopher Yang
  • 3,769
  • 4
  • 30
  • 27
  • Thanks nice one, but it does nt works on JSF app.. should it be different syntax ? – Jay Sep 07 '16 at 10:00
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
}
Peter Chaula
  • 3,456
  • 2
  • 28
  • 32
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

Muddassar
  • 349
  • 2
  • 13
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;
    }
mcool
  • 457
  • 4
  • 29