zoukankan      html  css  js  c++  java
  • springmvc集成shiro登录失败处理

      一般的登录流程会有:用户名不存在,密码错误,验证码错误等..

      在集成shiro后,应用程序的外部访问权限以及访问控制交给了shiro来管理。

      shiro提供了两个主要功能:认证(Authentication)和授权(Authorization);认证的作用是证明自身可以访问,一般是用户名加密码,授权的作用是谁可以访问哪些资源,通过开发者自己的用户角色权限系统来控制。

      shiro的会话管理和缓存管理不在本文范围内。

      下面通过登录失败的处理流程来介绍springmvc与shiro的集成。

      项目依赖:

    依赖名称  版本
    spring 4.1.4.RELEASE
    shiro 1.2.2
    self4j 1.7.5
    log4j 1.2.17

    在web.xml里配置shiro

        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>shiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

    新建一个spring-context-shiro.xml配置shiro相关信息,使用spring加载

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"
        default-lazy-init="true">
    
        <description>Shiro Configuration</description>
        <!-- 安全认证过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
            <property name="loginUrl" value="/sys/login" />
            <property name="successUrl" value="/sys" />
            <property name="filters">
                <map>
              <!--自定义登录验证过滤器-->
    <entry key="authc" value-ref="formAuthenticationFilter" /> </map> </property> <property name="filterChainDefinitions"> <value> /sys/login = authc /sys/logout = logout /sys/** = user </value> </property> </bean> <!-- 定义 Shiro 主要业务对象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="systemAuthorizingRealm" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> <!-- 会话ID生成器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 会话管理器,设定会话超时及保存 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 全局会话超时时间(单位毫秒),默认30分钟 --> <property name="globalSessionTimeout" value="1800000" /> <property name="sessionDAO" ref="sessionDAO"/> </bean> <!-- 会话验证调度器,每30分钟执行一次验证 --> <!-- <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler"> <property name="interval" value="1800000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- sessionDAO保存认证信息 --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value="shiro-activeSessionCache" /> <property name="cacheManager" ref="shiroCacheManager" /> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 用户授权信息Cache, 采用EhCache --> <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManager" /> </bean> <!-- Shiro生命周期处理器 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法级权限检查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>

    新建一个登录认证过滤器FormAuthenticationFilter.java

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.web.util.WebUtils;
    import org.springframework.stereotype.Service;
    
    /**
     * 表单验证(包含验证码)过滤类*/
    @Service
    public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
        public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
    
        private String captchaParam = DEFAULT_CAPTCHA_PARAM;
    
        public String getCaptchaParam() {
            return captchaParam;
        }
    
        protected String getCaptcha(ServletRequest request) {
            return WebUtils.getCleanParam(request, getCaptchaParam());
        }
    
        protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
            String username = getUsername(request);
            String password = getPassword(request);
            String locale = request.getParameter("locale");
            
            if (password == null) {
                password = "";
            }
            boolean rememberMe = isRememberMe(request);
            String host = getHost(request);
            String captcha = getCaptcha(request);
            return new UsernamePasswordToken(username, password.toCharArray(),locale, rememberMe, host, captcha);
        }
    }

    新建令牌类UsernamePasswordToken.java

    package com.chunhui.webservice.modules.sys.security;
    
    /**
     * 用户和密码(包含验证码)令牌类*/
    public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
        private static final long serialVersionUID = 1L;
        private String captcha;
        private String locale;
        
        public String getCaptcha() {
            return captcha;
        }
    
        public void setCaptcha(String captcha) {
            this.captcha = captcha;
        }
    
        public String getLocale() {
            return locale;
        }
    
        public void setLocale(String locale) {
            this.locale = locale;
        }
    
        public UsernamePasswordToken() {
            super();
        }
    
        public UsernamePasswordToken(String username, char[] password, boolean rememberMe, String host, String captcha) {
            super(username, password, rememberMe, host);
            this.captcha = captcha;
        }
        public UsernamePasswordToken(String username, char[] password, String locale,boolean rememberMe, String host, String captcha) {
            super(username, password, rememberMe, host);
            this.captcha = captcha;
            this.locale = locale;
        }
    }

    最后一个是认证实现类SystemAuthorizationRealm:

    package com.chunhui.webservice.modules.sys.security;
    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import javax.annotation.PostConstruct;
    
    import com.chunhui.webservice.common.utils.EmployeeType;
    import com.chunhui.webservice.common.utils.VertifyStatus;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.cache.Cache;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.SimplePrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.stereotype.Service;
    import com.chunhui.webservice.common.servlet.ValidateCodeServlet;
    import com.chunhui.webservice.common.utils.SpringContextHolder;
    import com.chunhui.webservice.modules.sys.entity.Employee;
    import com.chunhui.webservice.modules.sys.entity.Menu;
    import com.chunhui.webservice.modules.sys.service.SystemService;
    import com.chunhui.webservice.modules.sys.utils.SystemUtils;
    import com.chunhui.webservice.modules.sys.web.LoginController;
    
    /**
     * 系统安全认证实现类*/
    @Service
    @DependsOn({ "employeeDao", "roleDao", "menuDao" })
    public class SystemAuthorizingRealm extends AuthorizingRealm {
        private SystemService systemService;
    
        /**
         * 认证回调函数, 登录时调用
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    // 判断验证码
            Session session = SecurityUtils.getSubject().getSession();
            // 设置独立的session会话超时时间  session.setTimeout(60000);
            String code = (String) session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
            if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)) {
                throw new CaptchaException("验证码错误!");
            }
        
         //如果帐号不存在,输出 //throw new UnknownAccountException(); //如果帐号被禁用,输出 //throw new DisabledAccountException(); //保存登录时选择的语言 SecurityUtils.getSubject().getSession().setAttribute("locale", token.getLocale()); try{ SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(new Principal(employee), employee.getPassword(), getName()); return info; }catch (Throwable t){ t.printStackTrace(); throw new AuthenticationException(); } }/** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Principal principal = (Principal) getAvailablePrincipal(principals); Employee employee = getSystemService().getEmployeeByName(principal.getUsername()); if (employee != null) { SystemUtils.putCache("employee", employee); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List<Menu> list = SystemUtils.getMenuList(); for (Menu menu : list) { if (StringUtils.isNotBlank(menu.getPermission())) { // 添加基于Permission的权限信息 for (String permission : StringUtils.split(menu.getPermission(), ",")) { info.addStringPermission(permission); } } } // 更新登录IP和时间 getSystemService().updateEmployeeLoginInfo(employee.getId()); return info; } else { return null; } } /** * 清空用户关联权限认证,待下次使用时重新加载 */ public void clearCachedAuthorizationInfo(String principal) { SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName()); clearCachedAuthorizationInfo(principals); } /** * 清空所有关联认证 */ public void clearAllCachedAuthorizationInfo() { Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); if (cache != null) { for (Object key : cache.keys()) { cache.remove(key); } } } /** * 获取系统业务对象 */ public SystemService getSystemService() { if (systemService == null) { systemService = SpringContextHolder.getBean(SystemService.class); } return systemService; } /** * 授权用户信息 */ public static class Principal implements Serializable { private static final long serialVersionUID = 1L; private String id; private String username; private String realname; private Map<String, Object> cacheMap; public Principal(Employee employee) { this.id = employee.getId(); this.username = employee.getUsername(); this.realname = employee.getRealname(); } public String getId() { return id; } public String getUsername() { return username; } public String getRealname() { return realname; } public Map<String, Object> getCacheMap() { if (cacheMap == null) { cacheMap = new HashMap<String, Object>(); } return cacheMap; } } }

    那么在JSP页面,可以通过获取登录异常具体的异常类型来在页面显示错误原因

            <%String error = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);%>
           <c:set var="exp_type" value="<%=error %>"/>
                <c:set var="tips" value=""></c:set>
                <c:if test="${fn:contains(exp_type,'CaptchaException')}">
                    <c:set var="tips" value="验证码错误"></c:set>
                </c:if>
                <c:if test="${fn:contains(exp_type,'FailVertifyException')}">
                    <c:set var="tips" value="该账号审核未通过,不允许登陆!"></c:set>
                </c:if>
                <c:if test="${fn:contains(exp_type,'NotVertifyException')}">
                    <c:set var="tips" value="该账号正在审核中... 不允许登陆!"></c:set>
                </c:if>
                <c:if test="${fn:contains(exp_type,'UnknownAccountException')}">
                    <c:set var="tips" value="账号不存在!"></c:set>
                </c:if>
                <c:if test="${fn:contains(exp_type,'DisabledAccountException')}">
                    <c:set var="tips" value="账号不允许登陆!"></c:set>
                </c:if>
                <c:if test="${fn:contains(exp_type,'IncorrectCredentialsException')}">
                    <c:set var="tips" value="密码错误!"></c:set>
                </c:if>

     

  • 相关阅读:
    spark第一篇--简介,应用场景和基本原理
    一个Cmake的例子
    smarty 快速上手
    smarty 学习记录
    学习记录
    二进制八进制 十六进制
    高并发访问和海量数据 大型网站架构技术一览
    nosql简述
    微擎/微赞开发示例 学习记录
    好博客收藏
  • 原文地址:https://www.cnblogs.com/nosqlcoco/p/5579081.html
Copyright © 2011-2022 走看看