How to redirect to the homepage if the user accesses the login page after being logged in?

后端 未结 6 1325
一生所求
一生所求 2020-11-28 13:16

Here is my spring security config:




        
相关标签:
6条回答
  • 2020-11-28 14:12

    You could also restrict your login page to ROLE_ANONYMOUS and set an <access-denied-handler />:

    <access-denied-handler ref="accessDeniedHandler" />
    <intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />
    

    And in your handler check if the user is already authenticated:

    @Service
    public class AccessDeniedHandler extends AccessDeniedHandlerImpl {
        private final String HOME_PAGE = "/index.html";
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) {
                response.sendRedirect(HOME_PAGE);
            }
    
            super.handle(request, response, e);
        }
    }
    
    0 讨论(0)
  • 2020-11-28 14:12

    You can try checking

    if(SecurityContextHolder.getContext().getAuthentication() == null)
    

    True means the user isn't authenticated, and thus can be sent to the login page. I don't know how robust/reliable this is, but it seems reasonable to try.

    0 讨论(0)
  • 2020-11-28 14:13
    <http pattern="/login" auto-config="true" disable-url-rewriting="true">
      <intercept-url pattern="/login" access="ROLE_ANONYMOUS"/>
      <access-denied-handler error-page="/index.jsp"/>
    </http>
    
    0 讨论(0)
  • 2020-11-28 14:17

    Implement a Redirect Interceptor for this purpose:

    The Interceptor (implementing HandlerInterceptor interface) check if someone try to access the login page, and if this person is already logged in, then the interceptor sends a redirect to the index page.

    public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {    
    
        private String[] loginPagePrefixes = new String[] { "/login" };
    
        private String redirectUrl = "/index.html";
    
        private UrlPathHelper urlPathHelper = new UrlPathHelper();
    
        @Override
        public boolean preHandle(HttpServletRequest request,
                                 HttpServletResponse response,
                                 Object handler) throws Exception {
    
            if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
                               && isAuthenticated()) {
                response.setContentType("text/plain");
                sendRedirect(request, response);
                return false;
            } else {
                return true;
            }
        }
    
        private boolean isAuthenticated() {
            Authentication authentication =
                            SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null) {
                return false;
            }
            if (authentication instanceof AnonymousAuthenticationToken) {
                return false;
            }
            return authentication.isAuthenticated();
        }
    
        private void sendRedirect(HttpServletRequest request,
                                  HttpServletResponse response) {
    
            String encodedRedirectURL = response.encodeRedirectURL(
                                     request.getContextPath() + this.redirectUrl);
            response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
            response.setHeader("Location", encodedRedirectURL);
        }
    
        private boolean isInLoginPaths(final String requestUrl) {   
            for (String login : this.loginPagePrefixes) {
                if (requestUrl.startsWith(login)) {
                    return true;
                }
            }
            return false;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 14:19

    You can keep it simple flow by access-denied-page attribute in http element or as dtrunk said to write handler for access denied as well as. the config would be like

    <http access-denied-page="/403" ... >
     <intercept-url pattern="/login" access="ROLE_ANONYMOUS" />
     <intercept-url pattern="/user/**" access="ROLE_USER" />
     <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
     <form-login login-page="/login" default-target-url="/home" ... />
     ...
    </http>
    

    in controller for /403

    @RequestMapping(value = "/403", method = RequestMethod.GET)
    public String accessDenied() { //simple impl
        return "redirect:/home";
    }
    

    and for /home

    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String home(Authentication authentication) {
     // map as many home urls with Role
        Map<String, String> dashBoardUrls = new HashMap<String, String>();
        dashBoardUrls.put("ROLE_USER", "/user/dashboard");
        dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard");
    
        String url = null;
    
        Collection<? extends GrantedAuthority> grants = authentication
                .getAuthorities();
     // for one role per user
        for (GrantedAuthority grantedAuthority : grants) {
            url = dashBoardUrls.get(grantedAuthority.getAuthority());
        }
        if (url == null)
            return "/errors/default_access_denied.jsp";
    
        return "redirect:" + url;
    }
    

    and when you make request for /admin/dashboard without logged in, it will redirect /login automatically by security

    0 讨论(0)
  • 2020-11-28 14:21

    I've checked the topic more deeply than last time and found that you have to determine if user is authenticated by yourself in controller. Row Winch (Spring Security dev) says here:

    Spring Security is not aware of the internals of your application (i.e. if you want to make your login page flex based upon if the user is logged in or not). To show your home page when the login page is requested and the user is logged in use the SecurityContextHolder in the login page (or its controller) and redirect or forward the user to the home page.

    So solution would be determining if user requesting /auth/login is anonymous or not, something like below.

    applicationContext-security.xml:

    <http auto-config="true" use-expressions="true"
            access-decision-manager-ref="accessDecisionManager">
        <intercept-url pattern="/auth/login" access="permitAll" />
        <intercept-url pattern="/auth/logout" access="permitAll" />
        <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" />
        <intercept-url pattern="/**" access="XYZ_ACCESS" />
    
        <form-login login-page="/auth/login"
            authentication-failure-url="/auth/loginFailed"
            authentication-success-handler-ref="authenticationSuccessHandler" />
        <logout logout-url="/auth/logout" logout-success-url="/auth/login" />
    </http>
    
    <beans:bean id="defaultTargetUrl" class="java.lang.String">
        <beans:constructor-arg value="/content" />
    </beans:bean>
    
    <beans:bean id="authenticationTrustResolver"
            class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" />
    
    <beans:bean id="authenticationSuccessHandler"
            class="com.example.spring.security.MyAuthenticationSuccessHandler">
        <beans:property name="defaultTargetUrl" ref="defaultTargetUrl" />
    </beans:bean>
    

    Add to applicationContext.xml bean definition:

    <bean id="securityContextAccessor"
        class="com.example.spring.security.SecurityContextAccessorImpl" />
    

    which is class

    public final class SecurityContextAccessorImpl
          implements SecurityContextAccessor {
    
      @Autowired
      private AuthenticationTrustResolver authenticationTrustResolver;
    
      @Override
      public boolean isCurrentAuthenticationAnonymous() {
        final Authentication authentication =
            SecurityContextHolder.getContext().getAuthentication();
        return authenticationTrustResolver.isAnonymous(authentication);
      }
    }
    

    implementing simple interface

    public interface SecurityContextAccessor {
      boolean isCurrentAuthenticationAnonymous();
    }
    

    (SecurityContextHolder accessing code is decoupled from controller, I followed suggestion from this answer, hence SecurityContextAccessor interface.)

    And last but not least redirect logic in controller:

    @Controller
    @RequestMapping("/auth")
    public class AuthController {
      @Autowired
      SecurityContextAccessor securityContextAccessor;
    
      @Autowired
      @Qualifier("defaultTargetUrl")
      private String defaultTargetUrl;
    
      @RequestMapping(value = "/login", method = RequestMethod.GET)
      public String login() {
        if (securityContextAccessor.isCurrentAuthenticationAnonymous()) {
          return "login";
        } else {
          return "redirect:" + defaultTargetUrl;
        }
      }
    }
    

    Defining defaultTargetUrl String bean seems like a hack, but I don't have better way not to hardcode url... (Actually in our project we use <util:constant> with class containing static final String fields.) But it works after all.

    0 讨论(0)
提交回复
热议问题