精华内容
下载资源
问答
  • 往往一个类对象拥有多种权限,每种权限有两个状态即有和无,正常情况下,有多少个权限,就需要多少个字段保存相应状态,如果权限过多,那么这种方式显得极其笨重,最近学习了一种用一个int字段实现权限管理的方式...

    在实际开发中,往往一个类对象拥有多种权限,每种权限有两个状态即有和无,正常情况下,有多少个权限,就需要多少个字段保存相应状态,如果权限过多,那么这种方式显得极其笨重,最近学习了一种用一个int字段实现的权限管理的方式,方便快捷,实现原理简单,大大简化操作,用了一上午时间研究,简单实现了8个权限的管理(实际上int字段可以管理32位,与8位管理没有本质区别),现就实现过程及内部原理做一个简单总结

    1.需要了解的基础知识  ——JAJA中基本数据类型所占的空间:

    java基本数据类型 四类八种:byte(1字节),short(2字节),int(4字节),long(8字节),char(1字节),boolean(1字节),float(4字节),double(8字节),其中1字节=8位,所以 int类型在计算机内存中占32位

    2.需要了解的基础知识 ——JAVA中的位运算符,这里用到的 & (与) 、|(或) 、 ~(非)

    简单运算(都是二进制的数据)

    1 & 1 = 1                            1 | 1 = 1                     ~0 = 1

    1 & 0 = 0                            1 | 0 = 1                     ~1 = 0

    0 & 1 = 0                            0 | 1 = 1

    0 & 0 =0                             0 | 0 = 0

    了解以上基础知识后,简单的写一个小demo,尝试一下利用位运算管理权限的方便之处(如果是十进制的数参与位运算,首先将十进制的数转换为二进制,运算结束后再转为十进制,这些都是计算机自动完成的)

     

    public class Authority {
        //新增权限
        private static final int ALLOW_INSERT   = 1 << 0;
        //删除权限
        private static final int ALLOW_DELETE   = 1 << 1;
        //修改权限
        private static final int ALLOW_UPDATE   = 1 << 2;
        //查看权限
        private static final int ALLOW_SELECT   = 1 << 3;
        //可读权限
        private static final int ALLOW_READ     = 1 << 4;
        //可写权限
        private static final int ALLOW_WRITE    = 1 << 5;
        //复制权限
        private static final int ALLOW_COPY     = 1 << 6;
        //只读权限
        private static final int ALLOW_READONLY = 1 << 7;
        //用来保存当前存在的权限,即用这一个字段,保存8种权限状态
        private int state;
        //设置权限,1个或多个
        public void setAuto(int auto) {
            state =  auto;
        }
        //用来增加一个权限,一个或多个
        public void addAuto(int auto) {
            state = state | auto ;
        }
        //用来删除一个权限
        public void delAuto(int auto) {
            state = state &~auto;
        }
        //用来查看是否有某种权限
        public boolean isAllow(int auto) {
            return ((state & auto) == auto);
        }
        //用来查看是否没有某种权限
        public boolean isNotAllow(int auto) {
            return ((state & auto) == 0);
        }

    }

    public static void look(Authority auto) {
            System.out.println("ALLOW_INSERT   有权限:"+ auto.isAllow(ALLOW_INSERT));
            System.out.println("ALLOW_DELETE   有权限:"+ auto.isAllow(ALLOW_DELETE));
            System.out.println("ALLOW_UPDATE   有权限:"+ auto.isAllow(ALLOW_UPDATE));
            System.out.println("ALLOW_SELECT   有权限:"+ auto.isAllow(ALLOW_SELECT));
            System.out.println("ALLOW_READ     有权限:"+ auto.isAllow(ALLOW_READ));
            System.out.println("ALLOW_WRITE    有权限:"+ auto.isAllow(ALLOW_WRITE));
            System.out.println("ALLOW_COPY     有权限:"+ auto.isAllow(ALLOW_COPY));
            System.out.println("ALLOW_READONLY 有权限:"+ auto.isAllow(ALLOW_READONLY));
        }

     

     

    以上就是用int字段管理8个权限状态的demo全部代码,这8个权限的状态是我随便起的,不要在意这些细节

    写一个测试方法验证一下

    public static void main (String[] args){

        //测试一:设置某个对象拥有8种权限

        Authority auto = new Authority();

        auto.setAuto((1 << 8) -1);

        look(auto);

        打印结果显示所有权限都为  true

        //测试二 在所有权限都有的基础上,删除 增删改查权限

        auto.delAuto(ALLOW_INSERT | ALLOW_DELETE | ALLOW_UPDATE | ALLOW_SELECT);

         look(auto); 

        打印结果显示  增删改查权限为false

        //测试三  在测试二基础上增加 删除权限

       auto.addAuto(ALLOW_DELETE );

       look(auto); 

       打印结果显示删除权限增加成功

       到这里基本功能演示完毕,一个int字段可以保存多种状态,下面简单解释一下内部原理

       1.首先先定义8种状态,每种状态都是在二进制下位数左移得到的,即

          0000 0000 0000 0000 0000 0000 0000 0001     代表  1  << 0;

         由于这里演示8种状态,只写8位,前面的 0 全部省略

         0000 0001     1  << 0          ALLOW_INSERT

         0000 0010     1 << 1           ALLOW_DELETE

         0000 0100     1 << 2           ALLOW_UPDATE

         0000 1000     1 << 3           ALLOW_SELECT

         0001 0000     1 << 4           ALLOW_READ

         0010 0000     1 << 5           ALLOW_WRITE

         0100 0000     1 << 6           ALLOW_COPY

         1000 0000     1 << 7           ALLOW_READONLY

    以上不难看出,这8个位上,有1的代表有权限,有0的代表无权限,这样每个位分别控制一种权限,8个位的组合就可以实现8种权限的管理

    1.设置权限   setAuto(int auto){

                           state = auto;

                          }

    其实现是这样,传进去一个int值,一个值代表一个权限,可以传入多个。用位运算符号 “|” 连接参数 ,比如 传入增删权限

                        增权限     ALLOW_INSERT      其二进制形式  0000 0001

                        删权限     ALLOW_DELETE     其二进制形式  0000 0010

                                        根据位运算原理,两个二进制的数求或

                                                     0000 0001

                                             |       0000 0010

                                                     0000 0011        

                                         不难看出后两位增删权限都是1,这说明目前有增删权限,其余权限都为0即没有权限

    2.删除权限      public void delAuto(int auto) {
                               state = state &~auto;
                            }      

                            代码解释:设置state状态为      当前状态 与上 传进来的int值的求非,例如目前有增删权限,需要删除增权限

                            根据位运算原理        0000 0011   &   ~ 0000  0001         

                             第一步        ~ 0000  0001     的值为   1111 1110

                             第二部        0000 0011   &    1111 1110

                              0000 0011

                      &      1111 1110 

                              0000 0010

                         得到结果  0000 0010  可以看出,增加权限位上的数字为0 ,代表没有增权限,只有删权限

    3.查看是否拥某个有权限

                        public boolean isAllow(int auto) {
                            return ((state & auto) == auto);
                        }

            设当前 拥有增删权限       0000 0011

            传入int值        ALLOW_INSERT   其二进制是 0000 0001,代表查看是否有增权限

            (0000 0011 & 0000 0001)的值为 0000 0001

             上面提到,如果是位运算,计算机会将数字转为二进制计算,再将结果转为十进制

             ==是逻辑运算符,需要转成十进制计算

             0000 0001 的十进制是1 ,传入的参数 0000 0001 也是 1   ,1 == 1  结果为true 

            即0000 0011有增权限,其他方法也是通过这种位运算计算得出的结果,不一一演示

                  

          

     

    }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 【laravel-admin】权限管理实现原理

    千次阅读 2019-07-05 15:30:53
    laravel-admin权限管理于实现原理我们先使用laravel-admin后台实现一个权限管理解析权限管理实现权限管理数据表权限总结 这是第一次使用Markdown来写文章,以后也就使用这个来写了,那么今天呢!我们要写的是laravel...


    这是第一次使用Markdown来写文章,以后也就使用这个来写了,那么今天呢!我们要写的是laravel-admin后台的权限管理,还有权限管理实现的原理

    我们先使用laravel-admin后台实现一个权限管理

    1.添加一个用户管理的权限
    在这里插入图片描述
    标识(slug) 是用来标记权限的唯一标识,全局唯一。 名称(name) 是这个权限的展示名称,要让人一眼看明白这个权限是做什么用的
    标识(slug) 是用来标记权限的唯一标识,全局唯一。 名称(name) 是这个权限的展示名称,要让人一眼看明白这个权限是做什么用的
    2.添加一个角色
    在这里插入图片描述
    3.创建管理员
    在这里插入图片描述
    这样的话我们就有了kaka这个用户,然后我们来登录一下,我们只给了这个kaka一个用户管理的权限,这样就设置成功了
    在这里插入图片描述

    解析权限管理

    实现权限管理数据表

    在这里插入图片描述
    1.我们先看看admin_user表,这只是一个单纯保存后台管理员的一个表
    在这里插入图片描述
    2.下来就是我们的角色表,也就是我们在laravel-admin后台添加角色就会添加到这个表里边
    在这里插入图片描述
    3.下来就是重点来了,我们的角色用户表
    我们可以看到角色用户表,里边有个角色id跟用户id的外键,这个表就是把管理员跟角色联系在一起了
    在这里插入图片描述
    4.下来就是权限表,这个表是存储的所有的权限
    加粗样式
    5.最后就是我们的角色权限表了,跟角色管理这
    在这里插入图片描述

    权限总结

    上边给大家看了具体的表结构,其实这个不是和复杂,我们在简单的阐述一下
    首先我们在做权限管理的时候为了什么,那么就是希望不同的管理员看到不同的内容
    1.那么我们需要的第一个表就是后台管理员表admin_users,这个表只是用来存储管理员账号和密码
    2.在一个基本那就是我们的角色表admin_roles,这个表的性质跟管理员表是一样的也是来存储角色的信息
    3.那么这个时候就需要一个表来关联我们的admin_usersadmin_roles表,那就是admin_role_users,这个表只是一个中间件表,里边只需要把管理员跟角色的对应关系存储即可
    4.上边都完善了后我们就需要一个权限表了admin_permissions,这个表就是存储我们所有的权限
    5.那么我们的权限是不是需要跟角色关联呢!只有这样用户才可以从自己的角色中获取到自己拥有的权限,那么就需要一个权限角色表admin_role_permissions

    展开全文
  • shiro权限管理基本原理实现的整理

    千次阅读 2019-05-31 17:11:22
    shiro权限管理基本原理实现的整理 引言:这两天学习了一个对权限管理的新的框架shiro,在这里做一个总结,既为了帮助有需要的人,也方便自己以后来回顾。 本篇文章主要针对下面几个关键点来说明: 1. shiro简介 2....

    shiro权限管理基本原理和实现的整理


    引言:这两天学习了一个对权限管理的新的框架shiro,在这里做一个总结,既为了帮助有需要的人,也方便自己以后来回顾。
    本篇文章主要针对下面几个关键点来说明:
    1. shiro简介
    2. 集成spring,快速搭建环境
    3. shiro认证(即登录,重点)
    4. shiro授权(重点)
    5. shiro会话管理(Session)
    6. shiro缓存(remember Me)

    下面就让我来根据这六点,详细的说明一下shiro的基础原理和操作实现。
    一、shiro简介
    (1)什么是shiro?
    Apache Shiro 是 Java 的一个安全(权限)框架。
    • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在
    JavaSE 环境,也可以用在 JavaEE 环境。
    • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、 缓存
    等。
    • 下载:下载地址
    shiro的架构:
    在这里插入图片描述
    二.shiro集成spring所做的准备工作,本次的案例是集成SpringMvc,一些基本的配置已经忽略。

    1.导入jar包
      2.在web.xml里面加入shiro的过滤器

    	<!-- 
    	1. 配置  Shiro 的 shiroFilter.  
    	2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和 
    	<filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id. 
    	-->
        <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>
    

    3.创建二个shiro的配置文件applicationContext.xml和ehcache.xml

    在applicationContext.xml配置文件中,我们需要加入这么几样东西:
    1) 配置 SecurityManager!

     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="cacheManager" ref="cacheManager"/>
            <property name="authenticator" ref="authenticator"></property>
            
            <property name="realms">
            	<list>
        			<ref bean="jdbcRealm"/>
        			<ref bean="secondRealm"/>
        		</list>
            </property>
            
           <!--  <property name="rememberMeManager.cookie.maxAge" value="10"></property> -->
        </bean>
    

    2)配置 CacheManager.

    <--需要加入 ehcache 的 jar 包及配置文件. 
        -->     
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
                 will be creaed with a default config:
                 <property name="cacheManager" ref="ehCacheManager"/> -->
            <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
                 a specific Ehcache configuration to be used, specify that here.  If you don't, a default
                 will be used.: -->
            <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
        </bean>
    

    3)配置 Realm

      <!-- 
        	3. 配置 Realm 
        	3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
        -->     
        <bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
        	<property name="credentialsMatcher">
        		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        			<property name="hashAlgorithmName" value="MD5"></property>
        			<property name="hashIterations" value="1024"></property>
        		</bean>
        	</property>
        </bean>
        
       <bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">
        	<property name="credentialsMatcher">
        		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        			<property name="hashAlgorithmName" value="SHA1"></property>
        			<property name="hashIterations" value="1024"></property>
        		</bean>
        	</property>
        </bean>
    

    4)配置 LifecycleBeanPostProcessor

     4. 配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. 
        -->       
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
        <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
             the lifecycleBeanProcessor has run: -->
    

    5)启用 IOC 容器中使用 shiro 的注解.

      <!--  
        5. 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用. 
        -->     
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
              depends-on="lifecycleBeanPostProcessor"/>
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>
    

    6)配置 ShiroFilter.

    6.1 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
                          若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
        -->     
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager"/>
            <property name="loginUrl" value="/login.jsp"/>
            <property name="successUrl" value="/list.jsp"/>
            <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
            
           <!-- <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>  -->
            
            <!--  
            	配置哪些页面需要受保护. 
            	以及访问这些页面需要的权限. 
            	1). anon 可以被匿名访问
            	2). authc 必须认证(即登录)后才可能访问的页面. 
            	3). logout 登出.
            	4). roles 角色过滤器
            -->
          <property name = "filterChainDefinitions">
            	<value>
            		/login.jsp = anon
            		/shiro/login = anon
            		/shiro/logout = logout
            		
            		/user.jsp = roles[user]
            		/admin.jsp = roles[admin]
            		#everything else requires authentication
            		/** = authc
            	</value>
            </property>
    
        </bean>
    

    ---------》配置完了applicationContext.xml 我们接下来配置ehcache.xml
    ehcache.xml里面配置的都是接下来我们要说的关于缓存的一些信息,暂且可以忽略。

    <ehcache>
    
    
        <diskStore path="java.io.tmpdir"/>
        <!-- 缓存策略 -->
        <cache name="authorizationCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <cache name="authenticationCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <cache name="shiro-activeSessionCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            />
    
      
        <cache name="sampleCache1"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            overflowToDisk="true"
            />
    
        <cache name="sampleCache2"
            maxElementsInMemory="1000"
            eternal="true"
            timeToIdleSeconds="0"
            timeToLiveSeconds="0"
            overflowToDisk="false"
            /> -->
    
      
    
    </ehcache>
    
    

    4.代码实现。
    这里我将用几个简单的逻辑类和jsp页面来完成对登录授权访问的验证,为了方便,我先把全部代码粘上,后面一一解释。
    1)创建两个Realm实现类 继承AuthorizingRealm

    public class ShiroRealm extends AuthorizingRealm {
    
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    		System.out.println("[FirstReaml] doGetAuthenticationInfo");
    		//1. 将AuthenticationToken  强制转换成UsernamePasswordToken
    		UsernamePasswordToken uptoken =(UsernamePasswordToken)token;
    	
    		//2. 从UsernamePasswordToken 中来获取username
    		String username = uptoken.getUsername();
    		//3. 调用数据库中的方法,来查询username对应的记录
    		System.out.println("从数据库中获取"+username+"所对应的信息");
    		//4. 若用户不存在,则抛出异常
    		if("unknow".equals(username)) {
    			throw new 	UnknownAccountException("用户不存在!");
    		}
    		//5 根据用户信息的情况,决定是否需要抛出AuthenticationException的其他异常
    		if("monster".equals(username)){
    			throw new LockedAccountException("用户已被锁定");
    		}
    		//6. 根据用户情况,构建AuthenticationInfo 对象并且返回
    		//下面对象的三个参数是从数据库中获取的
    		//1.principal:认证的实体信息,可以是username,也可以是数据表对应的用户实体对象
    		Object  principal = username;
    		//2. credentials:密码
    		Object credentials =null;
    		if("admin".equals(username)) {
    			 credentials ="038bdaf98f2037b31f1e75b5b4c9b26e";
    		}else if("user".equals(username)) {
    			 credentials ="098d2c478e9c11555ce2823231e02ec1";
    		}
    		
    		//3.realmName:当前realm对象的name,调用父类的getName()方法即可
    		String realmName = getName();
    		//4.盐值
    		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
    		//SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(principal, credentials, realmName);
    		SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
    		return info;
    	}
    }
    
    public class SecondRealm extends AuthenticatingRealm {
    
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(
    			AuthenticationToken token) throws AuthenticationException {
    		System.out.println("[SecondReaml] doGetAuthenticationInfo");
    		
    		//1. 把 AuthenticationToken 转换为 UsernamePasswordToken 
    		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    		
    		//2. 从 UsernamePasswordToken 中来获取 username
    		String username = upToken.getUsername();
    		
    		//3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
    		System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");
    		
    		//4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
    		if("unknown".equals(username)){
    			throw new UnknownAccountException("用户不存在!");
    		}
    		
    		//5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常. 
    		if("monster".equals(username)){
    			throw new LockedAccountException("用户被锁定");
    		}
    		
    		//6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
    		//以下信息是从数据库中获取的.
    		//1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. 
    		Object principal = username;
    		//2). credentials: 密码. 
    		Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
    		if("admin".equals(username)){
    			credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
    		}else if("user".equals(username)){
    			credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
    		}
    		
    		//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
    		String realmName = getName();
    		//4). 盐值. 
    		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
    		
    		SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
    		info = new SimpleAuthenticationInfo("secondRealmName", credentials, credentialsSalt, realmName);
    		return info;
    	}
    }
    
    

    2)创建ShiroHandler,来处理我们要进行的逻辑

    @Controller
    @RequestMapping("/shiro/")
    public class ShiroHandler {
    	@Autowired
    	private ShiroService shiroService;
    	@RequestMapping("/testShiroannotation")
    	public String testShiroannotation(HttpSession session) {
    		shiroService.testMethod();
    		session.setAttribute("key","value13245");
    		return "redirect:/list.jsp";
    	}
    	
    	@RequestMapping("login")
    	public String login(@RequestParam("username")String username,@RequestParam("password")String password) {
    		System.out.println(password);
    		Subject currentUser = SecurityUtils.getSubject();
    		if(!currentUser.isAuthenticated()) {
    			UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    			token.setRememberMe(true);
    			try {
    				//执行登录
    				currentUser.login(token);
    			} catch (Exception e) {
    				System.out.println("登录失败:"+e.getMessage());
    			}
    		}
    		return "redirect:/list.jsp";
    	}
    }
    

    3)创建业务逻辑层service

    public class ShiroService {
    	@RequiresRoles(value={"admin"})
    	public void testMethod() {
    		System.out.println("test service time:"+new Date());
    		
    		Session session =SecurityUtils.getSubject().getSession();
    		Object  val = session.getAttribute("key");
    		System.out.println("Service SessionVal:"+val);
    	}
    }
    

    4)创建几个简单的网页,来实现登录、跳转、分配权限
    admin.jsp模拟管理员才能访问的页面

    <body>
    Admin Page
    </body>
    

    user.jsp模拟普通用户访问的页面

    <body>
    User Page
    </body>
    

    login.jsp模拟登录页面

    <body>
    Login Page
    
    <form action="shiro/login" method="post">
    	username:<input type="text" name="username">
    	<br><br>
    	password: <input type="password" name = "password">
    	<br><br>
    	<input type="submit"  value="Submit">
    </form>
    </body>
    

    unauthorized.jsp用于跳转到没有授权的页面

    <body>
    Unauthorized Page
    </body>
    

    list.jsp用来模拟登录成功跳转页面

    <body>
    <h4>List Page</h4> </br>
    
    WelCome:<shiro:principal></shiro:principal>
    
    	<shiro:hasRole name="admin">
    		</br>
    		<a href="admin.jsp">Admin Page</a></br>
    	</shiro:hasRole>
    	<shiro:hasRole name="user">
    		</br>
    		<a href="user.jsp">User Page</a></br>
    	</shiro:hasRole>
    	</br>
    	<a href="shiro/testShiroannotation">TestShiroannotation</a>
    	</br>
    	<a href="shiro/logout">Logout</a>
    </body>
    

    三、认证(因为上面代码都粘上了,所以我这里讲解的时候就截取关键部分)
     shiro的认证,说白了就是登录,我们在handler类里面有login方法
    图解:

    在这里插入图片描述
    角色:
     • 身份验证:一般需要提供如身份 ID 等一些标识信息来表明登录者的身
     份,如提供 email,用户名/密码来证明。
     • 在 shiro 中,用户需要提供 principals (身份)和 credentials(证
     明)给 shiro,从而应用能验证用户身份:
     • principals:身份,即主体的标识属性,可以是任何属性,如用户名、
     邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个
     Primary principals,一般是用户名/邮箱/手机号。
     • credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证
     书等。
     • 最常见的 principals 和 credentials 组合就是用户名/密码了
    流程:
     1.通过login.jsp页面收集到用户输入的username和password
     2.首先handler调用 Subject.login(token) 进行登录,其会自动委托给SecurityManager
     3. SecurityManager 负责真正的身份验证逻辑;它会委托给Authenticator 进行身份验证;
     4.Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
     5.Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用AuthenticationStrategy 进行多 Realm 身份验证;
     6. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处
    可以配置多个Realm,将按照相应的顺序及策略进行访问。
     7.创建Realm类接收handler传来的token,完成对用户名和密码的校验(Realm类上面代码已经创建)
    四、授权
    Shiro 支持三种方式的授权:
     1– 编程式:通过写if/else 授权代码块完成
     2– 注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常
     3– JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成
    授权的底层原理实现:
     • 1、首先调用 Subject.isPermitted*/hasRole* 接口,其会委托给SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
     • 2、 Authorizer是真正的授权者,如果调用如 isPermitted(“user:view”),其首先会通过• PermissionResolver 把字符串转换成相应的     Permission 实例;
     • 3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
     • 4、 Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个Realm,会委托给 ModularRealmAuthorizer 进行循环判断,
     如果匹配如 isPermitted*/hasRole* 会返回true,否则返回false表示 授权失败。
    两张验证有关的表格:
    在这里插入图片描述

    在这里插入图片描述

    (1)编程式的实验在上面realm类中代码已经体现,下面我截取出主要部分。

    		Object credentials =null;
    		if("admin".equals(username)) {
    			 credentials ="038bdaf98f2037b31f1e75b5b4c9b26e";
    		}else if("user".equals(username)) {
    			 credentials ="098d2c478e9c11555ce2823231e02ec1";
    		}
    

    (2)注解的方式实现,可以在service的相关方法上面写注解,也可以直接在handler或者controller类上面写注解。注意:当service类上面有@trationcal事务注解时,不能在service上写注解,只能在controller类上写注解。
    注解的话有下面几种,我们选择其中一个来举例:
     在这里插入图片描述
    我们可以为一个service中的某个方法写权限注解,来限制某个用户是否能访问某个方法,从而实现限制访问某个页面的目的。
    在这里插入图片描述
    同样,注解的方式也能在controller层使用
    在这里插入图片描述
    3)通过shiro标签在jsp页面进行授权
     这里通过list.jsp页面来进行页面访问的授权
     我们可以看到,当角色的身份谁是admin时,可以访问admin的页面,是user时,可以访问user的页面,但是由于我们前面代码给的权限,admin可以同时访问这两个页面,但是user就只能访问user的页面而不能访问admin的页面,剩下的没有给授权限制的页面,两种角色都能访问。在这里插入图片描述
     五、会话管理
      概述:Shiro 提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管 JavaSE 还是 JavaEE 环境都可以 使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web 的透明支持、 SSO 单点登录的支持 等特性。
      shiro对实现session的增删改查操作的主要接口是sessionDao。
      在这里插入图片描述
      如果我们需要对session实现增删改查操作的话,那么我们需要增加如下配置,并且创建SessionDao的类在这里插入图片描述
      如果不需要实现对session的操作,那么我们来简单示范一下:
      (1)我们在controller中对用HttpSession对session添加key value。

    	@RequestMapping("/testShiroannotation")
    	public String testShiroannotation(HttpSession session) {
    		shiroService.testMethod();
    		session.setAttribute("key","value13245");
    		return "redirect:/list.jsp";
    	}
    
    	(2)我们在service中用shiro的session来获取到我们前面存的键值对
    
    		
    		Session session =SecurityUtils.getSubject().getSession();
    		Object  val = session.getAttribute("key");
    		System.out.println("Service SessionVal:"+val);
    

    六、shiro缓存
     
     (1)缓存的话分两种缓存:
     1.Realm 缓存
    • Shiro 提供了 CachingRealm,其实现了CacheManagerAware 接口,提供了缓存的一些基础实现;
    • AuthenticatingRealm 及 AuthorizingRealm 也分别提供了对AuthenticationInfo 和 AuthorizationInfo 信息的缓
    存。
      2.Session 缓存
    • 如 SecurityManager 实现了 SessionSecurityManager,其会判断 SessionManager 是否实现了CacheManagerAware 接口,如果实现了会把CacheManager 设置给它。
    • SessionManager 也会判断相应的 SessionDAO(如继承自CachingSessionDAO)是否实现了CacheManagerAware,如果实现了会把 CacheManager设置给它
    • 设置了缓存的 SessionManager,查询时会先查缓存,如果找不到才查数据库。
    我们常用的缓存就是rememberMe
    (2)rememberMe
    概述:
    • Shiro 提供了记住我(RememberMe)的功能,比如访问如淘宝等一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
      • 1、首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会把 RememberMe 的Cookie 写到客户端并
      保存下来;
      • 2、关闭浏览器再重新打开;会发现浏览器还是记住你的;
      • 3、访问一般的网页服务器端还是知道你是谁,且能正常访问;
      • 4、但是比如我们访问淘宝时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
      这个比较简单,我们只需要在controller中添加如下代码:

    if(!currentUser.isAuthenticated()) {
    			UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    			token.setRememberMe(true);
    

    我们还可以在配置文件中设置时长:
    在applicationContext.xml中的securityManager bean中添加如下代码,value就是代表生效的时长,以秒为单位。

    <property name="rememberMeManager.cookie.maxAge" value="10"></property>
    

    以上就是我对shiro基础的一些介绍,因为还没有时间深入研究,讲的都比较浅显,等后面掌握之后还会更新内容。

    展开全文
  • 知识清单 1.了解基于资源的权限管理方式 2. 掌握权限数据模型 3. 掌握基于url的权限管理(不使用Shiro权限框架的...只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制。按照安全规则或安全策

    知识清单

    1.了解基于资源的权限管理方式
    2. 掌握权限数据模型
    3. 掌握基于url的权限管理(不使用Shiro权限框架的情况下实现权限管理)
    4. shiro实现用户认证
    5. shiro实现用户授权
    6. shiro与企业web项目整合开发的方法

    权限管理原理知识

    什么是权限管理

    只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制。按照安全规则或安全策略控制用户可以访问而且只能访问自己被授权的资源。
    权限管理包括用户认证和用户授权两部分。

    用户认证

    用户认证概念

    用户认证—— 用户去访问系统,系统需要验证用户身份的合法性。最常用的用户身份认证方法:1.用户密码方式、2.指纹打卡机、3.基于证书的验证方法。系统验证用户身份合法,用户方可访问系统的资源。

    用户认证流程


    关键对象

    subject:主体,理解为用户,可能是程序,都要去访问系统的资源,系统需要对subject进行身份认证。
    principal:身份信息,通常是唯一的,一个主体可以有多个身份信息,但是只能有一个主身份信息(primary  principal)。
    credential:凭证信息,可以是密码、证书、指纹等。
    总结:主体在进行身份认证时需要提供身份信息和凭证信息。

    用户授权

    用户授权概念

    用户授权,简单理解为访问控制,在用户认证通过后,系统对用户访问资源进行控制,当用户具有资源的访问权限方可访问。

    授权流程


    其中橙色为授权流程

    关键对象

    授权的过程可以理解为  who  对 what(which) 进行how操作
    who:主体,即subject,subject在认证通过后,系统进行访问控制。
    what(which):资源(Resource) ,subject必须具备资源访问权限才可以访问该资源。资源包括很多方面比如:用户列表页面、商品修改菜单、商品id为001的商品信息。
    资源分为资源类型和资源实例
    例如系统的用户信息就是资源类型,相当于Java类。
    系统中id为001的用户就是资源实例,相当于new的Java对象。
    how:权限/许可(permission),针对资源的权限或许可,subject必须具有permission方可访问资源,如何访问/操作需要定义permission,权限比如:用户添加、用户添加、商品删除。

    权限模型

    主体(账号、密码)
    资源(资源名称,访问地址)
    权限(权限名称、资源id)
    角色(角色名称)
    角色和权限关系(角色id、权限id)
    如下图:

    通常企业开发中将资源和权限合并为一张权限表,如下:
    资源(资源名称、访问地址)
    权限(权限名称、资源id)
    合并为:
    权限(权限名称、资源名称、资源访问地址)

    上图被称为权限管理的通用模型,不过在企业开发中根据系统自身特点还会对上图进行修改,但是用户、角色、权限、用户角色关系、角色权限关系是必不可少的。

    分配权限

    用户需要分配相应的权限才可以访问相应的资源。权限是对资源的操作许可。
    通常给用户分配资源权限需要将权限信息持久化,比如存储在关系数据库中。
    把用户信息、权限管理、用户分配的权限信息写入到数据库(权限数据模型)。

    权限控制(授权核心)

    基于角色的访问控制

    RBAC (Role  based access  control) 基于角色的访问控制
    比如:
    系统角色包括:部门经理、总经理...(角色针对用户进行划分)
    系统中代码实现:
    //如果该user是部门经理则可以访问if中的代码
    if(user.getRole("部门经理")){
        // 系统资源内容
        // 用户报表查看
    }
    问题:
    角色是针对人进行划分的,人作为用户在系统中属于活动内容,如果该角色可以访问的资源出现变更,则需要修改代码,比如:需要变更为部门经理和总经理都可以进行用户报表查看,代码改为:
    if(user.getRole("部门经理") || user.getRole("总经理")){
        // 系统资源内容
        // 用户报表查看
    }
    由此可以发现基于角色的访问控制是不利于系统维护的(可扩展性不强)

    基于资源的访问控制

    RBAC (Resource  based  access control)  基于资源的访问控制
    资源在系统中是不变的,比如资源有:类中的方法,页面中的按钮
    对资源的访问需要具有permission权限,代码可以写为:
    if(user.hasPermission("用户报表查看(权限标识符)")){
        // 系统资源内容
        // 用户报表查看
    }
    上面的方法就可以解决用户角色变更而不用修改上边权限控制的代码。
    如果需要变更权限只需要在分配权限模块去操作,给部门经理或总经理增加或解除权限
    建议使用基于资源的访问控制实现权限管理。

    权限管理解决方案

    什么是粗粒度权限和细粒度权限?

    粗粒度权限管理,是对资源类型的管理,资源类型比如:菜单、url连接、用户添加页面、用户信息、类方法、页面中按钮。
    粗粒度权限管理比如:超级管理员可以访问用户添加页面、用户信息等全部页面。
    部门管理员可以访问用户信息页面,包括页面中所有按钮。

    细粒度的权限管理,对资源实例的权限管理。资源实例就是资源类型的具体化,比如:用户id为001的修改连接,1110班的用户信息、行政部的员工。
    细粒度的权限管理就是数据级别的权限管理。
    细粒度权限管理比如:部门经理只可以访问本部门的员工信息,用户只可以看到自己的菜单,大区经理只能查看本辖区的销售订单...

    粗粒度和细粒度例子:
    系统中有一个用户查询页面,对用户列表查询分权限,如粗粒度管理,张三和李四都有用户列表查询的权限,张三和李四都可以访问用户列表查询。
    进一步进行细粒度的管理,张三(行政部)和李四(开发部)只可以查询自己本部门的用户信息,张三只能查看行政部的用户信息,李四只能查询开发部门的用户信息。细粒度的权限管理就是数据级别的权限管理。

    如何实现粗粒度和细粒度的权限管理

    如何实现粗粒度的权限管理?
    粗粒度权限管理比较容易将权限管理代码抽取出来在系统架构级别统一管理。比如:通过SpringMVC的拦截器实现授权。
    如何实现细粒度的权限管理?
    对细粒度的权限管理在数据级别是没有共性可言的,针对细粒度的权限管理就是系统业务逻辑的一部分,如果在业务层去处理相对简单,如果将细粒度的权限管理统一在系统架构级别去抽取,比较困难,即使进行了抽取,功能也可能存在扩展性不全的弊端。建议细粒度权限管理放在业务层去控制。比如:部门经理只查询本部门员工信息,在Service接口提供一个部门id的参数,controller中根据当前用户信息得到该用户属于哪个部门,调用service时将部门id传入service,实现该用户只查询本部门的员工。

    基于url拦截的方式实现

    基于url拦截的方式实现在实际开发中是比较常用的一种方式。
    对于web系统,通过filter过滤器实现url拦截,也可以通过SpringMVC的拦截器实现基于URL的拦截。

    使用权限管理框架来实现

    对于粗粒度的权限管理,建议使用优秀的权限管理框架进行实现,节省开发成本,提高开发效率。
    Shiro就是一个优秀的权限管理框架。

    基于URL的权限管理

    基于url的权限管理流程



    搭建环境

    数据库

    MySQL数据库中创建表:用户表、角色表、权限表(实质是权限和资源的结合)、用户角色关系表、角色权限关系表

    新建数据库shiro, 为了节约测试时间,在SpringMVC+mybatis基础之上进行整合(导入以前的基本数据),并导入权限数据如下:

    有关权限的SQL脚本如下:
    shiro_sql_table.sql
    /*
    SQLyog v10.2 
    MySQL - 5.1.72-community : Database - shiro
    *********************************************************************
    */
    
    
    /*!40101 SET NAMES utf8 */;
    
    /*!40101 SET SQL_MODE=''*/;
    
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    /*Table structure for table `sys_permission` */
    
    CREATE TABLE `sys_permission` (
      `id` bigint(20) NOT NULL COMMENT '主键',
      `name` varchar(128) NOT NULL COMMENT '资源名称',
      `type` varchar(32) NOT NULL COMMENT '资源类型:menu,button,',
      `url` varchar(128) DEFAULT NULL COMMENT '访问url地址',
      `percode` varchar(128) DEFAULT NULL COMMENT '权限代码字符串',
      `parentid` bigint(20) DEFAULT NULL COMMENT '父结点id',
      `parentids` varchar(128) DEFAULT NULL COMMENT '父结点id列表串',
      `sortstring` varchar(128) DEFAULT NULL COMMENT '排序号',
      `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    /*Table structure for table `sys_role` */
    
    CREATE TABLE `sys_role` (
      `id` varchar(36) NOT NULL,
      `name` varchar(128) NOT NULL,
      `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    /*Table structure for table `sys_role_permission` */
    
    CREATE TABLE `sys_role_permission` (
      `id` varchar(36) NOT NULL,
      `sys_role_id` varchar(32) NOT NULL COMMENT '角色id',
      `sys_permission_id` varchar(32) NOT NULL COMMENT '权限id',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    /*Table structure for table `sys_user` */
    
    CREATE TABLE `sys_user` (
      `id` varchar(36) NOT NULL COMMENT '主键',
      `usercode` varchar(32) NOT NULL COMMENT '账号',
      `username` varchar(64) NOT NULL COMMENT '姓名',
      `password` varchar(32) NOT NULL COMMENT '密码',
      `salt` varchar(64) DEFAULT NULL COMMENT '盐',
      `locked` char(1) DEFAULT NULL COMMENT '账号是否锁定,1:锁定,0未锁定',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    /*Table structure for table `sys_user_role` */
    
    CREATE TABLE `sys_user_role` (
      `id` varchar(36) NOT NULL,
      `sys_user_id` varchar(32) NOT NULL,
      `sys_role_id` varchar(32) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    shiro_sql_table_data.sql
    /*
    SQLyog v10.2 
    MySQL - 5.1.72-community : Database - shiro
    *********************************************************************
    */
    
    
    /*!40101 SET NAMES utf8 */;
    
    /*!40101 SET SQL_MODE=''*/;
    
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    /*Data for the table `sys_permission` */
    
    insert  into `sys_permission`(`id`,`name`,`type`,`url`,`percode`,`parentid`,`parentids`,`sortstring`,`available`) values 
    (1,'权限','','',NULL,0,'0/','0','1'),(11,'商品管理','menu','/item/queryItem.action',NULL,1,'0/1/','1.','1'),
    (12,'商品新增','permission','/item/add.action','item:create',11,'0/1/11/','','1'),
    (13,'商品修改','permission','/item/editItem.action','item:update',11,'0/1/11/','','1'),
    (14,'商品删除','permission','','item:delete',11,'0/1/11/','','1'),
    (15,'商品查询','permission','/item/queryItem.action','item:query',11,'0/1/15/',NULL,'1'),
    (21,'用户管理','menu','/user/query.action','user:query',1,'0/1/','2.','1'),
    (22,'用户新增','permission','','user:create',21,'0/1/21/','','1'),
    (23,'用户修改','permission','','user:update',21,'0/1/21/','','1'),
    (24,'用户删除','permission','','user:delete',21,'0/1/21/','','1');
    
    /*Data for the table `sys_role` */
    
    insert  into `sys_role`(`id`,`name`,`available`) values 
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f28','商品管理员','1'),
    	('ebc9d647-c6f9-11e4-b137-0adc305c3f28','用户管理员','1');
    
    /*Data for the table `sys_role_permission` */
    
    insert  into `sys_role_permission`(`id`,`sys_role_id`,`sys_permission_id`) values 
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f21','ebc8a441-c6f9-11e4-b137-0adc305c','12'),
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f22','ebc8a441-c6f9-11e4-b137-0adc305c','11'),
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f24','ebc9d647-c6f9-11e4-b137-0adc305c','21'),
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f25','ebc8a441-c6f9-11e4-b137-0adc305c','15'),
    	('ebc9d647-c6f9-11e4-b137-0adc305c3f23','ebc9d647-c6f9-11e4-b137-0adc305c','22'),
    	('ebc9d647-c6f9-11e4-b137-0adc305c3f26','ebc8a441-c6f9-11e4-b137-0adc305c','13');
    
    /*Data for the table `sys_user` */
    
    insert  into `sys_user`(`id`,`usercode`,`username`,`password`,`salt`,`locked`) values 
    	('lisi','lisi','李四','bf07fd8bbc73b6f70b8319f2ebb87483','uiwueylm','0'),
    	('zhangsan','zhangsan','张三','cb571f7bd7a6f73ab004a70322b963d5','eteokues','0');
    
    /*Data for the table `sys_user_role` */
    
    insert  into `sys_user_role`(`id`,`sys_user_id`,`sys_role_id`) values 
    	('ebc8a441-c6f9-11e4-b137-0adc305c3f28','zhangsan','ebc8a441-c6f9-11e4-b137-0adc305c'),
    	('ebc9d647-c6f9-11e4-b137-0adc305c3f28','lisi','ebc9d647-c6f9-11e4-b137-0adc305c');
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    查看对应权限模型的数据如下:
    sys_user  用户表数据

    sys_role  角色表

    sys_permission 权限表

    sys_user_role  用户角色关系表

    sys_role_permission  角色权限关系表

    开发环境

    JDK1.8
    MyEclipse
    技术架构:SpringMVC+Mybatis+jQuery easyUI

    系统工程架构


    系统登录

    系统登录相当于用户身份认证,用户登录成功,要在Session中记录用户的身份信息。
    操作流程:
    用户进入登录页面。
    输入用户名和密码进行登陆。
    进行用户名和密码校验。
    如果校验通过,在Session中记录用户身份信息。

    用户的身份信息

    创建专门类用于记录用户身份信息。
    /**
     * 用户身份信息,存入Session  由于Tomcat正常关闭时会将Session序列化的本地硬盘上,所以实现Serializable接口
     * @author liuxun
     *
     */
    public class ActiveUser implements Serializable {
    	private String userid; //用户id(主键)
    	private String usercode; // 用户账号
    	private String username; // 用户姓名
    	....
            ....
    }

    mapper

    mapper接口:根据用户账号查询用户(sys_user)信息 (使用逆向工程生成权限相关的PO类和mapper接口)
    如下所示:
      
    将生成的代码拷贝到项目中

    service(进行用户名和密码校验)

    接口功能:根据用户的身份和密码进行认证,如果认证通过,返回用户身份信息。
    认证过程:
    根据用户身份(账号)查询数据库,如果查询不到 则抛出用户不存在
    对输入的密码和数据库密码进行比对,如果一致,认证通过。
    新建权限管理Service接口 添加身份认证方法
    /**
     * 认证授权服务接口
     * @author liuxun
     *
     */
    public interface SysService {
    	//根据用户的身份和密码进行认证,如果认证通过,返回用户身份信息
    	public ActiveUser authenticat(String usercode,String password) throws Exception;
    	
    	//根据用户账号查询用户信息
    	public SysUser findSysUserByUserCode(String userCode) throws Exception;
            ......
    }
    方法实现:
    public class SysServiceImpl implements SysService {
    	@Autowired
    	private SysUserMapper sysUserMapper;
    
    	public ActiveUser authenticat(String usercode, String password) throws Exception {
    
    		/**
    		 * 认证过程: 根据用户身份(账号)查询数据库,如果查询不到则用户不存在 
    		 * 对输入的密码和数据库密码进行比对,如果一致则认证通过
    		 */
    		// 根据用户账号查询数据库
    		SysUser sysUser = this.findSysUserByUserCode(usercode);
    
    		if (sysUser == null) {
    			// 抛出异常
    			throw new CustomException("用户账号不存在");
    		}
    
    		// 数据库密码(MD5加密后的密码)
    		String password_db = sysUser.getPassword();
    
    		// 对输入的密码和数据库密码进行比对,如果一致,认证通过
    		// 对页面输入的密码进行MD5加密
    		String password_input_md5 = new MD5().getMD5ofStr(password);
    		if (!password_db.equalsIgnoreCase(password_input_md5)) {
    			//抛出异常
    			throw new CustomException("用户名或密码错误");
    		}
    		//得到用户id
    		String userid = sysUser.getId();
    		
    		//认证通过,返回用户身份信息
    		ActiveUser activeUser = new ActiveUser();
    		activeUser.setUserid(userid);
    		activeUser.setUsercode(usercode);
    		activeUser.setUsername(sysUser.getUsername());
    
    		return activeUser;
    	}
    
    	public SysUser findSysUserByUserCode(String userCode) throws Exception {
    		SysUserExample sysUserExample = new SysUserExample();
    		SysUserExample.Criteria criteria = sysUserExample.createCriteria();
    		criteria.andUsercodeEqualTo(userCode);
    
    		List<SysUser> list = sysUserMapper.selectByExample(sysUserExample);
    		if (list != null && list.size() > 0) {
    			return list.get(0);
    		}
    
    		return null;
    	}
       
           ......
    }
    配置Service,往类Service中使用@Autowire 需要注册Service 注册有两种方法(注解或配置文件),在架构时没有配置扫描Service  需要在配置文件中注册Service
    <!-- 认证和授权的Service -->
       <bean id="sysService" class="liuxun.ssm.service.impl.SysServiceImpl"></bean>

    controller(记录Session)

    //用户登录提交方法
    	@RequestMapping("/login")
    	public String login(HttpSession session,String randomcode,String usercode,String password) throws Exception{
    		// 校验验证码,防止恶性攻击
    		// 从Session中获取正确的验证码
    		String validateCode = (String) session.getAttribute("validateCode");
    		
    		//输入的验证码和Session中的验证码进行对比
    		if (!randomcode.equalsIgnoreCase(validateCode)) {
    			//抛出异常
    			throw new CustomException("验证码输入错误");
    		}
    		
    		//调用Service校验用户账号和密码的正确性
    		ActiveUser activeUser = sysService.authenticat(usercode, password);
    		
    		//如果Service校验通过,将用户身份记录到Session
    		session.setAttribute("activeUser", activeUser);
    		//重定向到商品查询页面
    		return "redirect:/first.action";
    	}

    用户认证拦截器

    anonymousURL.properties配置匿名URL

    配置可以匿名访问的URL


    编写身份认证拦截器

    //用于用户认证校验、用户权限校验
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            
    		//得到请求的url
    		String url = request.getRequestURI();
    		
    		//判断是否是公开地址
    		//实际开发中需要将公开地址配置在配置文件中
    		//从配置文件中取出可以匿名访问的URL
    		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");
    		for (String open_url : open_urls) {
    			if (url.indexOf(open_url)>=0) {
    				//如果是公开地址 则放行
    				return true;
    			}
    		}
    		
    		//判断用户身份在Session中是否存在
    		HttpSession session = request.getSession();
    		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
    		//如果用户身份在session中存在则放行
    		if (activeUser!=null) {
    			return true;
    		}
    		//执行到这里拦截,跳转到登录页面,用户进行身份认证
    		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
    		
    		//如果返回false表示拦截器不继续执行handler,如果返回true表示放行
    		return false;
    	}

    配置认证拦截器

    <!-- 拦截器 -->
    	<mvc:interceptors>
    	    <mvc:interceptor>
    		   	<!-- 用户认证拦截 -->
    		   	<mvc:mapping path="/**"/>
    		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
    	    </mvc:interceptor>
    	</mvc:interceptors>

    用户授权

    commonURL.properties配置公用访问地址

    在此配置文件中配置公用访问地址,公用访问地址只需要通过用户认证,不需要对公用访问地址分配权限即可访问。

    获取用户权限范围的菜单

    思路:
    在用户认证时,认证通过,根据用户id从数据库获取用户权限范围内的菜单,将菜单的集合存储在Session中。
    编辑存储用户身份信息的类ActiveUser 如下所示:
    public class ActiveUser implements Serializable {
    	private String userid; //用户id(主键)
    	private String usercode; // 用户账号
    	private String username; // 用户姓名
    	
    	private List<SysPermission> menus; //菜单
            //......setter和getter方法
    }
    自定义权限Mapper
    因为使用逆向工程生成的Mapper是不建议去修改的 因为它的代码联系非常紧密,一旦修改错误 就会牵一发而动全身。所以需要自定义一个权限的Mapper(SysPermissionMapperCustom)
    SysPermissionMapperCustom.xml中添加根据用户id查询用户权限的菜单
    <!-- 根据用户id查询菜单 -->
    <select id="findMenuListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">
       SELECT 
    	  * 
    	FROM
    	  sys_permission 
    	WHERE TYPE = 'menu' 
    	  AND id IN 
    	  (SELECT 
    	    sys_permission_id 
    	  FROM
    	    sys_role_permission 
    	  WHERE sys_role_id IN 
    	    (SELECT 
    	      sys_role_id 
    	    FROM
    	      sys_user_role 
    	    WHERE sys_user_id = #{userid}))
    </select>
    SysPermissionMapperCustom.java接口中添加对应的方法
    public interface SysPermissionMapperCustom {
        //根据用户id查询菜单
    	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;
      .......
    }
    在权限Service接口中添加对应的方法 在实现中注入SysPermissionMapperCustom
    SysServiceImpl.java中添加如下内容
    @Override
    	public List<SysPermission> findMenuListByUserId(String userid) throws Exception {
    		return sysPermissionMapperCustom.findMenuListByUserId(userid);
    	}

    获取用户权限范围的URL

    思路:
    在用户认证时,认证通过后,根据用户id从数据库中获取用户权限范围的URL,将URL的集合存储在Session中。
    修改ActiveUser 添加URL的权限集合
    public class ActiveUser implements Serializable {
    	private String userid; //用户id(主键)
    	private String usercode; // 用户账号
    	private String username; // 用户姓名
    	
    	private List<SysPermission> menus; //菜单
    	private List<SysPermission> permissions; //权限
    	//...setter和getter方法
    }
    SysPermissionMapperCustom.xml中添加根据用户id查询用户权限的URL
    <!-- 根据用户id查询URL -->
    <select id="findPermissionListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">
       SELECT 
    	  * 
    	FROM
    	  sys_permission 
    	WHERE TYPE = 'permission' 
    	  AND id IN 
    	  (SELECT 
    	    sys_permission_id 
    	  FROM
    	    sys_role_permission 
    	  WHERE sys_role_id IN 
    	    (SELECT 
    	      sys_role_id 
    	    FROM
    	      sys_user_role 
    	    WHERE sys_user_id = #{userid}))
    </select>
    SysPermissionMapperCustom.java接口中添加对应的方法
    //根据用户id查询权限URL
    	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
    SysServiceImpl.java中添加如下内容
    @Override
    	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception {
    		return sysPermissionMapperCustom.findPermissionListByUserId(userid);
    	}

    用户认证通过后取出菜单和URL放入Session

    修改权限SysServiceImpl中用户认证方法的代码
    //得到用户id
    		String userid = sysUser.getId();
    		//根据用户id查询菜单
    		List<SysPermission> menus = this.findMenuListByUserId(userid);
    		//根据用户id查询权限url
    		List<SysPermission> permissions = this.findPermissionListByUserId(userid);
    		
    		//认证通过,返回用户身份信息
    		ActiveUser activeUser = new ActiveUser();
    		activeUser.setUserid(userid);
    		activeUser.setUsercode(usercode);
    		activeUser.setUsername(sysUser.getUsername());
    		
            //放入权限范围的菜单和url
    		activeUser.setMenus(menus);
    		activeUser.setPermissions(permissions);

    菜单动态显示

    <c:if test="${activeUser.menus!=null }">
    				<ul>
    				<c:forEach items="${activeUser.menus }" var="menu">
    					<li><div>
    						<a title="${menu.name }" ref="1_1" href="#"
    							rel="${baseurl }/${menu.url }" icon="icon-log"><span
    							class="icon icon-log"> </span><span class="nav"><a href=javascript:addTab('${menu.name }','${baseurl }/${menu.url }')>${menu.name }</a></span></a>
    					</div></li>
    				</c:forEach>
    				</ul>
    			</c:if>

    授权拦截器

    public class PermissionInterceptor implements HandlerInterceptor{
    	//在执行handler之前执行的
    	//用于用户认证校验、用户权限校验
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            
    		//得到请求的url
    		String url = request.getRequestURI();
    		
    		//判断是否是公开地址
    		//实际开发中需要将公开地址配置在配置文件中
    		//从配置文件中取出可以匿名访问的URL
    		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");
    		for (String open_url : open_urls) {
    			if (url.indexOf(open_url)>=0) {
    				//如果是公开地址 则放行
    				return true;
    			}
    		}
    		
    		//从配置文件中获取公用访问url
    		List<String> common_urls = ResourcesUtil.getKeyList("commonURL");
    		//遍历公用地址 如果是公开地址则放行
    		for (String common_url : common_urls) {
    			if (url.indexOf(common_url)>0) {
    				//如果是公开,则放行
    				return true;
    			}
    		}
    		
    		//判断用户身份在Session中是否存在
    		HttpSession session = request.getSession();
    		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
    		//从Session中取出权限范围的URL
    		List<SysPermission> permissions = activeUser.getPermissions();
    		for (SysPermission sysPermission : permissions) {
    			//权限url
    			String permission_url = sysPermission.getUrl();
    			if (url.indexOf(permission_url)>0) {
    				return true;
    			}
    		}
    		
    		//执行到这里拦截,跳转到无权访问的提示页面
    		request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
    		
    		//如果返回false表示拦截器不继续执行handler,如果返回true表示放行
    		return false;
    	}
       ......
    }

    配置授权拦截器

    注意:要将授权拦截器配置在用户认证拦截器的下边,这是因为SpringMVC拦截器的放行方法是顺序执行的,如果是Struts的话则正好相反。
    <!-- 拦截器 -->
    	<mvc:interceptors>
    	    <mvc:interceptor>
    		   	<!-- 用户认证拦截 -->
    		   	<mvc:mapping path="/**"/>
    		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
    	    </mvc:interceptor>
    	    <mvc:interceptor>
    	    	<!-- 资源拦截 -->
    	    	<mvc:mapping path="/**"/>
    	    	<bean class="liuxun.ssm.controller.interceptor.PermissionInterceptor"></bean>
    	    </mvc:interceptor>
    	</mvc:interceptors>
    运行测试:
    其关键代码如下:
    PO类ActiveUser.java 存放用户身份和权限信息的类
    package liuxun.ssm.po;
    
    import java.io.Serializable;
    import java.util.List;
    
    /**
     * 用户身份信息,存入Session  由于Tomcat正常关闭时会将Session序列化的本地硬盘上,所以实现Serializable接口
     * @author liuxun
     *
     */
    public class ActiveUser implements Serializable {
    	private static final long serialVersionUID = 1L;
    	
    	private String userid; //用户id(主键)
    	private String usercode; // 用户账号
    	private String username; // 用户姓名
    	
    	private List<SysPermission> menus; //菜单
    	private List<SysPermission> permissions; //权限
        // 提供对应setter和getter方法
        ......
    }
    自定义权限的Mapper 
    SysPermissionMapperCustom.java
    package liuxun.ssm.mapper;
    
    import java.util.List;
    import liuxun.ssm.po.SysPermission;
    import liuxun.ssm.po.SysPermissionExample;
    import org.apache.ibatis.annotations.Param;
    /**
     * 权限mapper
     * @author liuxun
     *
     */
    public interface SysPermissionMapperCustom {
        //根据用户id查询菜单
    	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;
    	//根据用户id查询权限URL
    	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
    }
    SysPermissionMapperCustom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="liuxun.ssm.mapper.SysPermissionMapperCustom">
    
    <!-- 根据用户id查询菜单 -->
    <select id="findMenuListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">
       SELECT 
    	  * 
    	FROM
    	  sys_permission 
    	WHERE TYPE = 'menu' 
    	  AND id IN 
    	  (SELECT 
    	    sys_permission_id 
    	  FROM
    	    sys_role_permission 
    	  WHERE sys_role_id IN 
    	    (SELECT 
    	      sys_role_id 
    	    FROM
    	      sys_user_role 
    	    WHERE sys_user_id = #{userid}))
    </select>
    <!-- 根据用户id查询URL -->
    <select id="findPermissionListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">
       SELECT 
    	  * 
    	FROM
    	  sys_permission 
    	WHERE TYPE = 'permission' 
    	  AND id IN 
    	  (SELECT 
    	    sys_permission_id 
    	  FROM
    	    sys_role_permission 
    	  WHERE sys_role_id IN 
    	    (SELECT 
    	      sys_role_id 
    	    FROM
    	      sys_user_role 
    	    WHERE sys_user_id = #{userid}))
    </select>
    </mapper>
    自定义权限的Service接口以及实现类
    SysService.java
    package liuxun.ssm.service;
    
    import java.util.List;
    
    import liuxun.ssm.po.ActiveUser;
    import liuxun.ssm.po.SysPermission;
    import liuxun.ssm.po.SysUser;
    
    /**
     * 认证授权服务接口
     * @author liuxun
     *
     */
    public interface SysService {
    	//根据用户的身份和密码进行认证,如果认证通过,返回用户身份信息
    	public ActiveUser authenticat(String usercode,String password) throws Exception;
    	
    	//根据用户账号查询用户信息
    	public SysUser findSysUserByUserCode(String userCode) throws Exception;
    	
    	//根据用户id查询权限范围内的菜单
    	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;
    	
    	//根据用户id查询权限范围内的url
    	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
    }
    
    SysServiceImpl.java
    package liuxun.ssm.service.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import liuxun.ssm.exception.CustomException;
    import liuxun.ssm.mapper.SysPermissionMapperCustom;
    import liuxun.ssm.mapper.SysUserMapper;
    import liuxun.ssm.po.ActiveUser;
    import liuxun.ssm.po.SysPermission;
    import liuxun.ssm.po.SysUser;
    import liuxun.ssm.po.SysUserExample;
    import liuxun.ssm.service.SysService;
    import liuxun.ssm.util.MD5;
    
    public class SysServiceImpl implements SysService {
    	@Autowired
    	private SysUserMapper sysUserMapper;
    	
    	@Autowired
    	private SysPermissionMapperCustom sysPermissionMapperCustom;
    
    	public ActiveUser authenticat(String usercode, String password) throws Exception {
    
    		/**
    		 * 认证过程: 根据用户身份(账号)查询数据库,如果查询不到则用户不存在 
    		 * 对输入的密码和数据库密码进行比对,如果一致则认证通过
    		 */
    		// 根据用户账号查询数据库
    		SysUser sysUser = this.findSysUserByUserCode(usercode);
    
    		if (sysUser == null) {
    			// 抛出异常
    			throw new CustomException("用户账号不存在");
    		}
    
    		// 数据库密码(MD5加密后的密码)
    		String password_db = sysUser.getPassword();
            
    		// 对输入的密码和数据库密码进行比对,如果一致,认证通过
    		// 对页面输入的密码进行MD5加密
    		String password_input_md5 = new MD5().getMD5ofStr(password);
    		if (!password_db.equalsIgnoreCase(password_input_md5)) {
    			//抛出异常
    			throw new CustomException("用户名或密码错误");
    		}
    		//得到用户id
    		String userid = sysUser.getId();
    		//根据用户id查询菜单
    		List<SysPermission> menus = this.findMenuListByUserId(userid);
    		//根据用户id查询权限url
    		List<SysPermission> permissions = this.findPermissionListByUserId(userid);
    		
    		//认证通过,返回用户身份信息
    		ActiveUser activeUser = new ActiveUser();
    		activeUser.setUserid(userid);
    		activeUser.setUsercode(usercode);
    		activeUser.setUsername(sysUser.getUsername());
    		
            //放入权限范围的菜单和url
    		activeUser.setMenus(menus);
    		activeUser.setPermissions(permissions);
    		
    		return activeUser;
    	}
    
    	public SysUser findSysUserByUserCode(String userCode) throws Exception {
    		SysUserExample sysUserExample = new SysUserExample();
    		SysUserExample.Criteria criteria = sysUserExample.createCriteria();
    		criteria.andUsercodeEqualTo(userCode);
    
    		List<SysUser> list = sysUserMapper.selectByExample(sysUserExample);
    		if (list != null && list.size() > 0) {
    			return list.get(0);
    		}
    
    		return null;
    	}
    	
    	@Override
    	public List<SysPermission> findMenuListByUserId(String userid) throws Exception {
    		return sysPermissionMapperCustom.findMenuListByUserId(userid);
    	}
    
    	@Override
    	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception {
    		return sysPermissionMapperCustom.findPermissionListByUserId(userid);
    	}
    }
    登录控制器
    package liuxun.ssm.controller;
    
    import javax.servlet.http.HttpSession;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import liuxun.ssm.exception.CustomException;
    import liuxun.ssm.po.ActiveUser;
    import liuxun.ssm.service.SysService;
    
    /**
     * 登录和退出
     * @author liuxun
     *
     */
    @Controller
    public class LoginController {
        @Autowired
        private SysService sysService;
    	
    	//用户登录提交方法
    	@RequestMapping("/login")
    	public String login(HttpSession session,String randomcode,String usercode,String password) throws Exception{
    		// 校验验证码,防止恶性攻击
    		// 从Session中获取正确的验证码
    		String validateCode = (String) session.getAttribute("validateCode");
    		
    		//输入的验证码和Session中的验证码进行对比
    		if (!randomcode.equalsIgnoreCase(validateCode)) {
    			//抛出异常
    			throw new CustomException("验证码输入错误");
    		}
    		
    		//调用Service校验用户账号和密码的正确性
    		ActiveUser activeUser = sysService.authenticat(usercode, password);
    		
    		//如果Service校验通过,将用户身份记录到Session
    		session.setAttribute("activeUser", activeUser);
    		//重定向到商品查询页面
    		return "redirect:/first.action";
    	}
    	
    	//用户退出
    	@RequestMapping("/logout")
    	public String logout(HttpSession session) throws Exception{
    		//session失效
    		session.invalidate();
    		//重定向到商品查询页面
    		return "redirect:/first.action";
    	}
    }
    身份认证拦截器LoginInterceptor.java
    package liuxun.ssm.controller.interceptor;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import liuxun.ssm.po.ActiveUser;
    import liuxun.ssm.util.ResourcesUtil;
    
    /**
     * 测试拦截器1
     * @author liuxun
     *
     */
    public class LoginInterceptor implements HandlerInterceptor{
    	//在执行handler之前执行的
    	//用于用户认证校验、用户权限校验
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            
    		//得到请求的url
    		String url = request.getRequestURI();
    		
    		//判断是否是公开地址
    		//实际开发中需要将公开地址配置在配置文件中
    		//从配置文件中取出可以匿名访问的URL
    		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");
    		for (String open_url : open_urls) {
    			if (url.indexOf(open_url)>=0) {
    				//如果是公开地址 则放行
    				return true;
    			}
    		}
    		
    		//判断用户身份在Session中是否存在
    		HttpSession session = request.getSession();
    		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
    		//如果用户身份在session中存在则放行
    		if (activeUser!=null) {
    			return true;
    		}
    		//执行到这里拦截,跳转到登录页面,用户进行身份认证
    		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
    		
    		//如果返回false表示拦截器不继续执行handler,如果返回true表示放行
    		return false;
    	}
    
    	//在执行handler返回modelAndView之前执行
    	//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    			throws Exception {
    		System.out.println("HandlerInterceptor2...postHandle");
    	}
    
    	//执行handler之后执行此方法
    	//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长
    	//实现系统,统一日志记录
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)
    			throws Exception {
    		System.out.println("HandlerInterceptor2...afterCompletion");
    	}
    
    }
    资源授权拦截器PermissionInterceptor
    package liuxun.ssm.controller.interceptor;
    
    import java.security.acl.Permission;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import liuxun.ssm.po.ActiveUser;
    import liuxun.ssm.po.SysPermission;
    import liuxun.ssm.util.ResourcesUtil;
    
    /**
     * 授权拦截器
     * @author liuxun
     *
     */
    public class PermissionInterceptor implements HandlerInterceptor{
    	//在执行handler之前执行的
    	//用于用户认证校验、用户权限校验
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            
    		//得到请求的url
    		String url = request.getRequestURI();
    		
    		//判断是否是公开地址
    		//实际开发中需要将公开地址配置在配置文件中
    		//从配置文件中取出可以匿名访问的URL
    		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");
    		for (String open_url : open_urls) {
    			if (url.indexOf(open_url)>=0) {
    				//如果是公开地址 则放行
    				return true;
    			}
    		}
    		
    		//从配置文件中获取公用访问url
    		List<String> common_urls = ResourcesUtil.getKeyList("commonURL");
    		//遍历公用地址 如果是公开地址则放行
    		for (String common_url : common_urls) {
    			if (url.indexOf(common_url)>0) {
    				//如果是公开,则放行
    				return true;
    			}
    		}
    		
    		//判断用户身份在Session中是否存在
    		HttpSession session = request.getSession();
    		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
    		//从Session中取出权限范围的URL
    		List<SysPermission> permissions = activeUser.getPermissions();
    		for (SysPermission sysPermission : permissions) {
    			//权限url
    			String permission_url = sysPermission.getUrl();
    			if (url.indexOf(permission_url)>0) {
    				return true;
    			}
    		}
    		
    		//执行到这里拦截,跳转到无权访问的提示页面
    		request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
    		
    		//如果返回false表示拦截器不继续执行handler,如果返回true表示放行
    		return false;
    	}
    
    	//在执行handler返回modelAndView之前执行
    	//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    			throws Exception {
    		System.out.println("HandlerInterceptor2...postHandle");
    	}
    
    	//执行handler之后执行此方法
    	//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长
    	//实现系统,统一日志记录
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)
    			throws Exception {
    		System.out.println("HandlerInterceptor2...afterCompletion");
    	}
    
    }
    拦截器配置
    <!-- 拦截器 -->
    	<mvc:interceptors>
    	    <mvc:interceptor>
    		   	<!-- 用户认证拦截 -->
    		   	<mvc:mapping path="/**"/>
    		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
    	    </mvc:interceptor>
    	    <mvc:interceptor>
    	    	<!-- 资源拦截 -->
    	    	<mvc:mapping path="/**"/>
    	    	<bean class="liuxun.ssm.controller.interceptor.PermissionInterceptor"></bean>
    	    </mvc:interceptor>
    	</mvc:interceptors>
    使用URL拦截总结:
    使用基于URL拦截的权限管理方式,实现起来比较简单,不依赖框架使用过滤器或拦截器就可以实现
    弊端:需要将所有的URL全部配置起来,比较繁琐,不易维护,URL(资源)和权限表示方式不规范

    展开全文
  • 我的通用权限系统设计是更换权限时候尽量不要涉及到代码修改,来自chinaunix论坛,今天转过来看看。... 复制代码 代码如下: /* *控制访问表 * acl值 功能 * 1 ...* 512 超级管理使用 */ class aclACL extends acl { public
  • 之前研究了1.x版本的LBE的一些实现安全监控的原理,基本摸清了它实现联网权限管理原理,这里做个简单记录。 首先要介绍一下Android应用程序启动的过程,这方面可以查阅一些相关资料了解详细信息,我这里只...
  • 权限管理原理知识

    2018-06-23 16:12:31
    1 权限管理原理知识1.1 什么是权限管理只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。权限管理包括用户...
  • Shiro基于URL的权限控制是通过Filter实现的,本篇从我们注入的ShiroFilterFactoryBean开始入手,翻看源码追寻Shiro中的过滤器的实现原理。 初始化流程 ShiroFilterFactoryBean实现了FactoryBean接口,那么Spring在...
  • 1、概述 管理系统、论坛或者一般网站的后台都需要做权限的控制...复杂的软件也有权限和业务逻辑分开的独立权限管理系统,@吉日嘎拉的通用权限管理系统就属于这种吧。因系统架构不同权限控制方式也不相同,如C/S架构...
  • 只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户认证和授权两部分。 1.2 用户认证 ...
  • 只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制。按照安全规则或安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户认证和用户授权两部分。 用户认证 用户认证...
  • 权限管理之一 原理

    千次阅读 2018-05-23 10:13:43
    只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户认证和授权两部分。一、用户认证: 用户...
  • 转:... 本文分析一个权限管理类Xposed模块的源代码,主要分析权限管理功能实现原理。完全按照本人看代码的顺序写成。写此文主要不是为了分析代码,而是总结这种分析代码的思路。所...
  • 只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户认证和授权两部分。 1.2 用户认证 ...
  •  只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户认证和授权两部分。  2.认证:  ...
  • 上篇博客中简单介绍了一下权限管理原理,其中包括最主要的两部分认证和授权。这篇博客中简单介绍一下shiro的认证和授权过程。   什么是shiro? Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能...
  • 拦截器实现权限管理

    2011-11-18 22:03:40
    拦截器实现权限管理,没与数据库交互,不过原理差不多了
  • 本文实例讲述了Mysql账户管理原理实现方法。分享给大家供大家参考,具体如下: 账户管理 在生产环境下操作数据库时,绝对不可以使用root账户连接,而是创建特定的账户,授予这个账户特定的操作权限,然后连接进行...
  • jenkins实现权限管理

    2019-06-07 21:09:24
    安装Role-based Authorization Strategy插件 ...原理是通过配置角色权限,然后再将用户绑定到对应角色上 首先进入manager role编辑角色权限 主要关注从项目上配置区分用户的权限,一般配置项目下有构建,取消...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,378
精华内容 551
关键字:

权限管理实现原理