After successfully logging in shiro, jump to custom error HTML problem

After successfully logging in shiro, jump to error html

  1. Let's talk about the problem first: in the saas project, after logging in successfully, you jump to error HTML, pause for a while and enter the main page.

  2. Tell me about my steps to deal with this problem:

  • Find out the login process of this page.
  • Causes and timing of problems
  1. According to the above ideas, first deal with the saas platform login process, as follows:
  • LoginController does not exist for the project. There are interfaces such as login, main and mianIndex. And the controller handles local exceptions:
/**
 * The exception handling in the current controller class is mainly used for user login. Do not write other operations in this class
 * 
 * @param req
 * @param e
 * @return
 * @throws Exception
 */
@ExceptionHandler(value = BusinessException.class)
public ModelAndView ErrorHandler(HttpServletRequest req, BusinessException e) throws Exception {
	ModelAndView view = new ModelAndView();
	view.addObject("msg", "Login failed" + e.getMsg());
	view.setViewName("error");
	return view;
}
  • shiro is used in the project, so Filter will be performed first when logging in. ShiroConfig and CustomAccessFilter exist in the project
  • CustomAccessFilter inherits FormAuthenticationFilter, so the login will first execute the re method: onAccessDenied, then execute to executeLogin, execute the rewritten onLoginSuccess, and finally execute the unfinished content in the onAccessDenied method.
/**
 *
 */
package cn.etcom.security.filter;

import java.util.Date;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import cn.etcom.entity.security.ActiveUser;
import cn.etcom.security.ShiroUtils;
import cn.etcom.util.prop.WebDefineProperties;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.util.WebUtils;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.etcom.entity.SystemLog;
import cn.etcom.service.log.SystemLogService;
import cn.etcom.service.log.impl.SystemLogServiceImpl;
import cn.etcom.util.LoggerUtils;
import cn.etcom.util.enums.ActionEnum;
import cn.etcom.util.spring.SpringUtil;

/**
 * @author admin
 *
 */
public class CustomAccessFilter extends FormAuthenticationFilter {
    private static final Logger logger = LoggerFactory.getLogger(LogoutFilter.class);

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) { // Determine whether to log in
            if (isLoginSubmission(request, response)) { // Judge whether it is post access
                SystemLogService systemLogService = SpringUtil.getBean(SystemLogServiceImpl.class);
                String ip = LoggerUtils.getCliectIp((HttpServletRequest) request);
                HttpSession session = ((HttpServletRequest) request).getSession();
                //Session session = ShiroUtils.getSession();
                session.setAttribute("ip", ip);
                String username = super.getUsername(request);
                WebDefineProperties wdp = SpringUtil.getBean(WebDefineProperties.class);
                String s = ShiroUtils.getSession().getId().toString();
                boolean flag = executeLogin(request, response);
                logger.debug("--------------User login system:{},false:Login succeeded, true:Login failed----------------------", flag);
                if (!flag) {

                    Subject subject = getSubject(request, response);
                    ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
//                    System.out.println(activeUser);
                    if (wdp.getKickout()) ShiroUtils.kickout(activeUser, s);
                    SystemLog systemLog = new SystemLog();
                    systemLog.setRequestip(ip).setUrl(request.getLocalAddr() + "/login").setHttpmethod("POST")
                            .setType("normal").setActionName("User login").setAction(ActionEnum.LOGIN.toString())
                            .setOpTime(new Date()).setOpUser(username).setDescription(ActionEnum.LOGIN + ":System login")
                            .setTenantId(activeUser != null ? activeUser.getTenantId() : "");
                    systemLogService.saveLog(systemLog);
                    return flag;
                } else {
                    return true;
                }
            } else {
                // The sessionID has been registered, but it has not been submitted by post
                return true;
            }
        } else {
            saveRequestAndRedirectToLogin(request, response);
        }
        return false;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//        if (isLoginRequest(request, response)) {
        return super.isAccessAllowed(request, response, mappedValue);
//        } else {
//            String host = request.getParameter("host");
//            if("1.1.1.1".equals(host) )
//
//                return true;
//             else
//                 return false;
//        }
        //return true;
    }

    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        WebUtils.getAndClearSavedRequest(request);
        WebUtils.redirectToSavedRequest(request, response, "/");
        return false;
    }
}

- shiro profile

package cn.etcom.security;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.etcom.security.filter.CustomAccessFilter;
import cn.etcom.security.filter.CustomLogoutFilter;
import cn.etcom.security.filter.SysUserFilter;
import cn.etcom.security.listener.ShiroSessionListener;
import cn.etcom.util.base.constant.Constants;
import cn.etcom.web.properties.FileUploadProperteis;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@Configuration
public class ShiroConfig {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    private final static String algorithmName = "md5";
    private final static int hashIterations = 2;

    private final static String CACHE_KEY = Constants.CACHE_KEY;
    private final static String SESSION_KEY = Constants.SESSION_KEY;
    private final static int EXPIRE = 1800;

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.password}")
    private String password;
    @Autowired
    private FileUploadProperteis fileUploadProperteis;

    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(this.host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    @Primary
    @Bean("redisCacheManager")
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        // If the cache is configured, the entity class placed in the session must have an id
        redisCacheManager.setPrincipalIdFieldName("userid");
        //redisCacheManager.setValueSerializer(myRedisSerializer());
        return redisCacheManager;
    }

    @Bean
    public ShiroSessionIdGenerator sessionIdGenerator() {
        return new ShiroSessionIdGenerator();
    }

    /*
        @Bean
        public MyRedisSerializer myRedisSerializer(){
            return new MyRedisSerializer();
        }
        */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setKeyPrefix(SESSION_KEY);
        redisSessionDAO.setExpire(EXPIRE);
        //  redisSessionDAO.setValueSerializer(myRedisSerializer());
        return redisSessionDAO;
    }

/*

    @Bean(name="ehCacheManager")
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile(cacheFile);
        return cacheManager;
    }
*/

/*

    @Bean
    public SimpleCookie getSimpleCookie() {
        SimpleCookie cookie = new SimpleCookie("shiro.sesssion");
        cookie.setMaxAge(EXPIRE);
        cookie.setPath("/");
        return cookie;
    }
*/


    @Bean("lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
/*

  @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
*/

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name = "credentialsMatcher")
    public RetryLimitHashedCredentialsMatcher hashedCredentialsMatcher() {
        RetryLimitHashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher();
        // Hash algorithm: MD5 algorithm is used here;
        hashedCredentialsMatcher.setHashAlgorithmName(algorithmName);
        hashedCredentialsMatcher.setHashIterations(hashIterations);
        return hashedCredentialsMatcher;
    }

    @Bean
    public CustomRealm realm() {
        CustomRealm shiroRealm = new CustomRealm();
        shiroRealm.setCachingEnabled(true);
        //Enable authentication cache, that is, cache AuthenticationInfo information. The default is false
        //shiroRealm.setAuthenticationCachingEnabled(true);
        shiroRealm.setAuthorizationCachingEnabled(true);
        //shiroRealm.setAuthenticationCacheName("authenticationCache");
        shiroRealm.setAuthorizationCacheName("authorizationCache");
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    @Bean
    public SessionManager getDefaultWebSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
     /*   Collection<SessionListener> listeners = new ArrayList<>();
        listeners.add(sessionListener());
        sessionManager.setSessionListeners(listeners);
        sessionManager.setGlobalSessionTimeout(EXPIRE);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionValidationInterval(10000);
        sessionManager.setSessionValidationSchedulerEnabled(true);*/
        //sessionManager.setSessionIdCookie(getSimpleCookie());
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    @Bean
    public SecurityManager securityManager() {

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        securityManager.setSessionManager(getDefaultWebSessionManager());
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }


    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro's core security interface
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        //shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        Map<String, Filter> m = new ConcurrentHashMap<>();
        // Log out
        m.put("logout", new CustomLogoutFilter());
        m.put("authc", new CustomAccessFilter());
        m.put("sysUser", new SysUserFilter());
        shiroFilterFactoryBean.setFilters(m);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put(fileUploadProperteis.getStaticPath(),"anon");
        filterChainDefinitionMap.put("/error", "anon");
        filterChainDefinitionMap.put("/outside/access", "anon");
        filterChainDefinitionMap.put("/outside/getToken", "anon");
        filterChainDefinitionMap.put("/auth", "anon");
        filterChainDefinitionMap.put("/login/sub", "anon");
        filterChainDefinitionMap.put("/profile/*", "anon");
        filterChainDefinitionMap.put("/swagger/**", "anon");
        filterChainDefinitionMap.put("/resources/**", "anon");
        filterChainDefinitionMap.put("/actuator", "anon");
        filterChainDefinitionMap.put("/actuator/health", "anon");
        filterChainDefinitionMap.put("/actuator/info", "anon");
        filterChainDefinitionMap.put("/dept/findAll", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * Online person statistics
     *
     * @return
     */
    @Bean("sessionListener")
    public ShiroSessionListener sessionListener() {
        ShiroSessionListener sessionListener = new ShiroSessionListener();
        return sessionListener;
    }
}

  1. When dealing with this problem, I locate the problem on the redirection path of the onLoginSuccess() method of Filter in the background.
    In fact, the reason for the problem is: there are some rules in shiro. The onLoginSuccess() method resets backward for the first time, and the browser path will be spliced:; JSESSIONID=XXXX, so special characters; Causes the access request to become: http://localhost:10003/;JSESSIONID=login_token_9b0ceaba-4448-469b-9652-a9eaae6abe10/.
    The exception 400 will be intercepted by the project exception, and then access error html.

In error There is a setTimeOut timer in HTML. Jump to window in 2 seconds location. href="/". This will access the index HTML, and then access the: / main interface if the login is successful. If the login fails, you will return to the login page.

  1. Handle problems according to problem points:
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setSessionIdUrlRewritingEnabled(false);
  1. Knowledge points learned:
  • The CustomAccessFilter we wrote inherits FormAuthenticationFilter. The actual method that needs to be rewritten at first is onAccessDenied(). Implement core processing. However, if the onLoginSuccess() method is not rewritten, the access path after successful login is: the original request in the previous session, that is, login. The browser will access: http://localhost:10003/login
    When rewriting this method, first clean up the original request address, and then redirect to "/". If there is no such request address; jsXXX will access index HTML, and then access / main to main html

  • The second point is; jsXXX is spliced in ShiroHttpServletResponse.

  • The key point to solve this problem is as follows:
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false);

Or: create a new MyShiroHttpServletResponse and inherit ShiroHttpServletResponse

Rewrite its methods encodeRedirectURL and toEncoded

  • 1
  • 1

Reference article address:

https://blog.csdn.net/ruanes/article/details/108417460

https://blog.csdn.net/weixin_34128534/article/details/92655476

https://blog.csdn.net/qq_39727959/article/details/106350249

https://www.cnblogs.com/sevenlin/p/sevenlin_shiro20150924.html

https://www.jianshu.com/p/a157c666159b

  1. 1

Keywords: Java Shiro Spring Boot Back-end login

Added by centerwork on Sat, 25 Dec 2021 11:41:48 +0200