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

后端 未结 4 778
小蘑菇
小蘑菇 2020-12-04 06:59

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.

相关标签:
4条回答
  • 2020-12-04 07:22

    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>
    
    0 讨论(0)
  • 2020-12-04 07:28

    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;
          }
     }
    
    0 讨论(0)
  • 2020-12-04 07:31

    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";
        }
    }
    
    0 讨论(0)
  • 2020-12-04 07:31

    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
    }
    
    0 讨论(0)
提交回复
热议问题