zoukankan      html  css  js  c++  java
  • JavaWeb项目:Shiro实现简单的权限控制(整合SSM)

    该demo整合Shiro的相关配置参考开涛的博客

    数据库表格相关设计

    这里写图片描述 
    表格设计得比较简单,导航栏直接由角色表auth_role的角色描述vRoleDesc(父结点)和角色相关权限中的权限描述(标记为导航结点)vPermissionDesc(展开子项)组成。 
    这里写图片描述

    Shiro相关设置

    密码输入错误次数限制

    //密码重试5次次数限制 
    public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher{
        private Cache<String,AtomicInteger> passwordRetryCache;
    
        public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager){
            passwordRetryCache=cacheManager.getCache("passwordRetryCache");
        }
    
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            String username=(String)token.getPrincipal();
            AtomicInteger retryCount=passwordRetryCache.get(username);
            if(retryCount==null){
                retryCount=new AtomicInteger(0);
                passwordRetryCache.put(username, retryCount);
            }
            if(retryCount.incrementAndGet()>5){
                throw new ExcessiveAttemptsException();
            }
            boolean matches= super.doCredentialsMatch(token, info);//匹配密码
            if(matches){
                passwordRetryCache.remove(username);
            }
            return matches;
        }
    }

    相关配置:

    <!-- 凭证(密码)匹配器 -->
        <bean id="credentialsMatcher" class="com.test.shiro.credentials.RetryLimitHashedCredentialsMatcher">
            <constructor-arg ref="cacheManager"/>
            <property name="hashAlgorithmName" value="md5"/><!--md5加密-->
            <property name="hashIterations" value="2"/><!--加密迭代两次-->
            <property name="storedCredentialsHexEncoded" value="true"/><!--为true时使用Hex编码(默认);false时使用Base64编码-->
        </bean>

    使用缓存实现为ehcache,相关配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache name="shiroCache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
       xsi:noNamespaceSchemaLocation="ehcache.xsd">
        <diskStore path="java.io.tmpdir"/> 
        <cache name="passwordRetryCache"
            maxEntriesLocalHeap="2000"
            eternal="false"<!--非永久有效-->
            timeToIdleSeconds="3600"<!-- 对象空闲时间,即60min后失效-->
            timeToLiveSeconds="0"
            overflowToDisk="false"
            statistics="true">
        </cache>
        <!--...-->
    </ehcache>

    扩展AuthorizingRealm:用于从数据库抓取密码等相关验证信息和相关权限信息

    public class UserRealm extends AuthorizingRealm{
    
        @Autowired
        private UserService userService;
    
        //获取相关授权信息
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String userName=(String)principals.getPrimaryPrincipal();
    
            SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
            authorizationInfo.setRoles(userService.findPermissionsByUserName(userName));//获取角色信息
            authorizationInfo.setStringPermissions(userService.findPermissionsByUserName(userName));//获取权限信息
    
            return authorizationInfo;
        }
    
        //获取身份验证信息
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String userName=(String)token.getPrincipal();
            User user=userService.getUserByUserName(userName);//获取身份信息(密码和密码盐)
            if(user==null){
                throw new UnknownAccountException();
            }
            SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
                    user.getUserName(),
                    user.getPassword(),
                    ByteSource.Util.bytes(user.getUserName()+user.getPasswordSalt()),
                    getName());
            return authenticationInfo;
        }
    
    }

    登录相关

    扩展FormAuthenticationFilter:用于登录后获取用户导航栏,并将其存入session范围

    public class WithNavibarFormAuthenticationFilter extends FormAuthenticationFilter {
    
        @Autowired
        private UserService userService;
    
        @Override
        protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
                ServletResponse response) throws Exception {
            HttpServletRequest httpReq=(HttpServletRequest)request;
    
            String userName=(String)SecurityUtils.getSubject().getPrincipal();
            List navigationBar=userService.getNavigationBar(userName);
            httpReq.getSession().setAttribute("navibar", navigationBar);
            return super.onLoginSuccess(token, subject, request, response);
        }
    
    }

    登录Controller实现(用户密码不匹配情况下执行)

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @Autowired
        private UserService userService;
        //...
    
        @RequestMapping("/login")
        public ModelAndView login(HttpServletRequest req){
            String error=null;
            String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
            if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
                error = "用户名/密码错误";
            } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
                error = "用户名/密码错误";
            } else if(exceptionClassName != null) {
                error = "其他错误:" + exceptionClassName;
            }
            ModelAndView mav=new ModelAndView("login");
            mav.addObject("error", error);
            return mav;
        }
        //...
    }

    登录表单代码:注意提交action=”“,以及输入控件name值须与form表单过滤器中的设置对应

    <form class="form-signin" method="post" action="">
            <h3 class="form-signin-heading">请登录</h3>
            <label for="inputEmail" class="sr-only">用户名</label>
            <input type="text" id="inputEmail" class="form-control" placeholder="用户名" name="username" required autofocus>
            <label for="inputPassword" class="sr-only">密码</label>
            <input type="password" id="inputPassword" class="form-control" placeholder="密码" name="password" required>
            <div class="checkbox">
              <label>
                <input type="checkbox" name="rememberMe"> 记住我
              </label>
            </div>
            <p class="bg-warning">${error}</p>
            <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
          </form>

    最后通过Spring注解,控制访问

    @RequiresPermissions("user:list")
        @RequestMapping("/list")
        public ModelAndView showUserList(){
            //
        }

    完整shiro配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:util="http://www.springframework.org/schema/util"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <!-- 缓存管理器 使用Ehcache实现 -->
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:config/ehcache.xml"/>
        </bean>
        <!-- 凭证(密码)匹配器 -->
        <bean id="credentialsMatcher" class="com.test.shiro.credentials.RetryLimitHashedCredentialsMatcher">
            <constructor-arg ref="cacheManager"/>
            <property name="hashAlgorithmName" value="md5"/>
            <property name="hashIterations" value="2"/>
            <property name="storedCredentialsHexEncoded" value="true"/>
        </bean>  
        <!-- Realm实现 -->
        <bean id="userRealm" class="com.test.shiro.realm.UserRealm">
            <property name="credentialsMatcher" ref="credentialsMatcher"/>
            <property name="cachingEnabled" value="true"/>
            <property name="authenticationCachingEnabled" value="true"/>
            <property name="authenticationCacheName" value="authenticationCache"/>
            <property name="authorizationCachingEnabled" value="true"/>
            <property name="authorizationCacheName" value="authorizationCache"/>
        </bean>
        <!-- 会话ID生成器 -->
        <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
    
        <!-- 会话Cookie模板 -->
        <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <constructor-arg value="sid"/>
            <property name="httpOnly" value="true"/>
            <property name="maxAge" value="180000"/>
        </bean>
    
        <!-- 会话DAO -->
        <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
            <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
            <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
        </bean>
    
        <!-- 会话验证调度器 -->
        <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
            <property name="sessionValidationInterval" value="1800000"/>
            <property name="sessionManager" ref="sessionManager"/>
        </bean>
    
        <!-- 会话管理器 -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <property name="globalSessionTimeout" value="1800000"/>
            <property name="deleteInvalidSessions" value="true"/>
            <property name="sessionValidationSchedulerEnabled" value="true"/>
            <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
            <property name="sessionDAO" ref="sessionDAO"/>
            <property name="sessionIdCookieEnabled" value="true"/>
            <property name="sessionIdCookie" ref="sessionIdCookie"/>
        </bean>
    
        <!-- 安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="userRealm"/>
            <property name="sessionManager" ref="sessionManager"/>
            <property name="cacheManager" ref="cacheManager"/>
        </bean>
    
        <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
        <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
            <property name="arguments" ref="securityManager"/>
        </bean>
        <!-- 基于Form表单的身份验证过滤器 -->
        <bean id="formAuthenticationFilter" class="com.test.shiro.filter.WithNavibarFormAuthenticationFilter">
            <property name="usernameParam" value="username"/>
            <property name="passwordParam" value="password"/>
            <property name="rememberMeParam" value="rememberMe"/>
            <property name="loginUrl" value="/user/login"/><!--注意value值为登录url-->
        </bean>
        <!-- Shiro的Web过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager"/>
            <property name="loginUrl" value="/user/login"/>
            <property name="filters">
                <util:map>
                    <entry key="authc" value-ref="formAuthenticationFilter"/>
                </util:map>
            </property>
            <property name="filterChainDefinitions">
                <value>
                    /=authc
                    /index=authc
                    /user/login=authc<!-- 使用表单filter authc -->
                    /logout=logout
                    /static/**=anon<!-- 不拦截静态资源文件 -->
                    /**=user
                </value>
            </property>
        </bean>
    
        <!-- Shiro生命周期处理器-->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
     </beans>

    其他如dao、service、controller组件实现省略

    完整代码见:https://github.com/weixupeng/auth-control_SSM

  • 相关阅读:
    今天18:40分左右一部价值500多块捷安特自行车被盗!
    利用ASP.net上传文件
    _desktop.ini
    Visual Studio .NET 设置移植工具
    审计厅的项目终于可以告一段落了
    Word2CHM Assistant(Word2CHM助手)V2.1.0 破解版
    最近比较烦!
    delphi 中 Format 用法总汇
    谈谈公司管理及需求方面的问题
    [待续]SQLSERVER无法访问远程服务器问题
  • 原文地址:https://www.cnblogs.com/weixupeng/p/8569643.html
Copyright © 2011-2022 走看看