shiro实际开发整理

1.1         shiro介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

三个核心组件:Subject, SecurityManager 和 Realms.

1、Subject

即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

2、SecurityManager

它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

3、Realm

Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

 

1.2         Shiro 体系结构


Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用

户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用

户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信

息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以

提高效率;

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能

把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录

了。

记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过

相应的接口注入给 Shiro 即可。

 

1.3         Shiro运行原理


Application Code:应用程序代码,就是我们自己的编码,如果在程序中需要进行权限控制,需要调用Subject的API.

Subject:主体,代表的了当前用户。所有的Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager,可以将Subject当成一个门面,而真正执行者是SecurityManager.(可参考1.4.4登录Controller)

SecurityManage:安全管理器,所有与安全有关的操作都会与SecurityManager交互,并且它管理所有的Subject.

Realm:域 shiro是从Realm来获取安全数据(用户,角色,权限)。就是说 SecurityManager要验证用户身份, 那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法(可参考1.4.4认证);也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作(可参考1.4.4授权);可以把 Realm 看成 DataSource,即安全数据源

 

从数据库中查询出的权限或角色与权限控制(权限控制方式可参考1.6)相匹配时通过验证

 

 

1.4     Shiro与spring集成

在企业开发中,使用spring集成shiro方案是常见的做法。

1.4.1maven依赖

 

<shiro.version>1.2.3</shiro.version>

 

<!-- shiro -->
<dependency>
   
<groupId>org.apache.shiro</groupId>
   
<artifactId>shiro-all</artifactId>
   
<version>${shiro.version}</version>
</
dependency>

 

1.4.2  在web.xml文件中配置

Shiro 提供了与 Web 集成的支持,其通过一个 ShiroFilter 入口来拦截需要安全控制的 URL,

然后进行相应的控制, ShiroFilter 类似于如 Strut2/SpringMVC 这种 web 框架的前端控制器,

其是安全控制的入口点,其负责读取配置(如 ini 配置文件),然后判断 URL 是否需要登

录/权限等工作。

 

<filter>

         <filter-name>shiroFilter</filter-name>

         <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>

</filter>

<filter-mapping>

         <filter-name>shiroFilter</filter-name>

         <url-pattern>/*</url-pattern>

</filter-mapping>

Xml配置基础权限,不进行ini单独配置    以下为加载xml

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:spring-mybatis.xml,classpath:spring-shiro.xml</param-value>
</context-param>

 

DelegatingFilterProxy作用是自动到 spring 容器查找名字为 shiroFilter(filter-name)的 bean

并把所有 Filter 的操作委托给它

1.4.3  在spring-shiro.xml文件中配置


<beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    
<!--注入安全管理器-->
    
<propertyname="securityManager"ref="securityManager"/>
    
<!--  privateString loginUrl; 当后边配置url,需要当前用户认证通过后才可以访问;如果用户没有登陆,跳转登陆页面
           private String successUrl; 认证通过,默认跳转页面,建议不配置,shiro认证成功自动到上一个请求路径
           private String unauthorizedUrl; 未经授权-->
    
<propertyname="loginUrl"value="/index.jsp"/>
    
<propertyname="unauthorizedUrl"value="/unauthorized.jsp"></property>

    
<!-- 注入过滤器链:配置系统中url请求拦截规则  注意:配置url拦截规则有顺序
       authc:过滤器工厂产生过滤器之一
    /**:所有请求
       当系统发送某url要求当前用户必须认证通过后才能访问
      anon:匿名过滤器:访问url不需要当前用户认证,没有权限可以访问
      perms:权限过滤器:访问url需要当前用户具有某个权限才可以访问
-->
    
<propertyname="filterChainDefinitions">
        
<value>
            
/index.jsp = anon  
             /datejs/** = anon
             /lib/** = anon
             /static/** = anon    静态资源需要放过,一些登录等不需权限也可访问的资源
             /login = anon
             /loginp =anon
             /welcome.html =anon

             /** =  authc
         </value>
    
</property>
 
</bean>

 

 

<!--配置安全管理器对象 -->
<beanid="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
   
<propertyname="realm"ref="userRealm"></property>
</
bean>

 

!-- 配置realm对象 -->
<beanid="userRealm"class="com.cn.xxx.shiro.UserRealm"></bean>   

<!-- spring提供配置shiro注解扫描
    在servie层方法上使用shiro注解,运行时期此方法所在类产生代理对象
    AutoProxy:自动代理,根据请求自动选择代理技术
        jdk动态代理:基于接口
        cglib动态代理:基于Class
    注意:强制使用cglib动态代理
 -->
<beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
   
<propertyname="proxyTargetClass"value="true"></property>
</
bean>
<!-- 配置shiro的切面:(切点+通知/增强)  增强:验证当前用户是否有权限 -->
<beanclass="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>

 

1.4.4自定义realm实现授权与认证

 

自定义realm一般是继承AuthorizingRealm,因为它提供了认证与授权功能

 

认证操作

@Override
protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throwsAuthenticationException {
   
System.out.println("开始认证");
   
UsernamePasswordTokenusernamePasswordToken = (UsernamePasswordToken) authenticationToken;
    //页面输入的用户名
   
//根据用户名查询数据库中真实密码
   
String username = usernamePasswordToken.getUsername();
   
User user = iUserService.queryAccountByName(username);
   
if(user==null){
       
//用户名输出错误
       
return null//当次方法中返回null,shiro会抛出异常 :未知账户异常
   
}
   
//比对密码工作交给shiro框架
    //p1:主角  p2:令牌/真实密码  p3:当前realm名称
   
AuthenticationInfo info = newSimpleAuthenticationInfo(user,user.getPwd(),this.getName());
   
return info;
}

授权操作

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    List<Permission> permissionList = new ArrayList<>();
    List<Role> roleList = new ArrayList<>();
    System.out.println("开始授权");
    //返回简单授权信息:包含当前用户有的权限点;角色
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

    //如果系统内置账户:管理员账户,有所有的权限以及角色
    Subject subject = SecurityUtils.getSubject();
    User user = (User) subject.getPrincipal();
    if("admin".equalsIgnoreCase(user.getAccount())){
        permissionList = permissionService.queryPermissionList();
    }else{

        //根据用户ID进行查询
        roleList = iUserService.queryRoleByID(user.getId());
        roleList.removeAll(Collections.singleton(null));
        if (!roleList.isEmpty()){
            List<Role> list = roleService.queryPermissionByRoleId(roleList.get(0).getRoleId());
            permissionList = list.get(0).getPermissionList();
        }
    }

    if(permissionList != null && !permissionList.isEmpty()){
        //添加用户权限
        for (Permission permission : permissionList) {
            if (permission.getPermissionUrl() == null || permission.getPermissionUrl().trim() == ""){
                continue;
            }
            info.addStringPermission(permission.getPermissionUrl());
        }
    }

    if (roleList != null && !roleList.isEmpty()){
        //添加用户角色
        for (Role role : roleList) {
            info.addRole(role.getRoleName());
        }
    }

    return info;              
}

 

 

登录Controller中调用: 通过认证的令牌,与登录的密码和账户进行匹配,如果登录失败,user为null

 

1.5    Shiro整合ehcache

1.       导入ehcache的maven依赖

<!--shiro缓存 ehcache-->
<dependency>
   
<groupId>net.sf.ehcache</groupId>
   
<artifactId>ehcache-core</artifactId>
   
<version>2.6.11</version>
</
dependency>

 

2.       在src/main/resource下提供ehcache的配置文件shiro-ehcache.xml

<ehcachename="shirocache">
      
<!-- diskStore:磁盘存储目录 -->
   
<diskStorepath="/opt/tomcat/shiroCache/ehcache"/>
   
<!-- 缓存策略:
      
maxElementsInMemory:内存中存储对象个数
       eternal:缓存数据是否永久有效
       timeToIdleSeconds:内存 中对象最大空闲时间
       timeToLiveSeconds:内存 中对象最大存活时间
       maxElementsOnDisk:磁盘中存储对象个数
       diskExpiryThreadIntervalSeconds:清除缓存数据线程执行周期单位:秒
       memoryStoreEvictionPolicy:清理缓存中对象策略  LRU:最近最少使用  FIFO:先进先出
        -->
   
<defaultCache
           
maxElementsInMemory="10000"
           
eternal="false"
           
timeToIdleSeconds="120"
           
timeToLiveSeconds="120"
           
maxElementsOnDisk="10000000"
           
diskExpiryThreadIntervalSeconds="120"
           
memoryStoreEvictionPolicy="LRU"
>
       
<persistencestrategy="localTempSwap"/>
   
</defaultCache>

 



</
ehcache>

 

3.      将ehcache缓存管理交给spring管理

 

<!--配置ehcache缓存管理器对象 -->
<beanid="cacheManager"class="org.apache.shiro.cache.ehcache.EhCacheManager">
   
<!-- 注入ehcache配置文件:配置缓存策略 -->
   
<propertyname="cacheManagerConfigFile"value="classpath:shiro-ehcache.xml"></property>
</
bean>

 

4.       注入缓存管理对象

<!--配置安全管理器对象 -->
<beanid="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
   
<propertyname="realm"ref="userRealm"></property>
   
<!-- 注入缓存管理对象 -->
   
<propertyname="cacheManager"ref="cacheManager"></property>
</
bean>

 

5.       在realm中引入缓存管理 因为使用的是默认配置,此处省略

<!-- 配置realm对象 -->
<beanid="userRealm"class="com.cn.xxx.shiro.UserRealm">xxx</bean>

 

1.6         Shiro总结

Shiro为程序共提供了四种权限控制方式

1.6.1             第一种 url级别权限控制


ShiroFilter具有上面10Filter校验功能!!!!

anon 不需要认证和登陆可以访问

authc 必须认证后才能访问

perms 必须具有某个权限才能访问

roles  必须具有某个角色才能访问

l  port 请求必须符合端口才能访问

l  rest 限制HTTP请求方式 post、get

l  ssl 必须用https才能访问

l  user 必须存在用户才能访问

存在用户(cookie)和认证(session)不一样

logout 必须session里面没有这个用户才能访问

 

<propertyname="filterChainDefinitions">   <value>       /index.jsp = anon        /datejs/** = anon        /lib/** = anon        /static/** = anon        /login = anon        /loginp =anon        /welcome.html =anon        /** =  authc   </value></property>

1.6.2             第二种 页面标签权限控制

        

 

例如:

<shiro:hasPermission name="user">
   <div class="menu_dropdown bk_2">
      <dl id="menu-article">
         <dt><i class="Hui-iconfont">&#xe616;</i>用户管理<i class="Hui-iconfont menu_dropdown-arrow">&#xe6d5;</i></dt>
         <dd>
            <ul><shiro:hasPermission name="/user/manage">
               <li><a data-href="${ctx}/user/manage" data-title="查看用户信息" href="javascript:void(0)">用户信息</a></li>
               </shiro:hasPermission>
               <shiro:hasPermission name="/user/roleManage">
               <li><a data-href="${ctx}/user/roleManage" data-title="角色管理" href="javascript:void(0)">角色管理</a></li>
               </shiro:hasPermission>
               <shiro:hasPermission name="/user/permissionManage">
                  <li><a data-href="${ctx}/user/permissionManage" data-title="权限管理" href="javascript:void(0)">权限管理</a></li>
               </shiro:hasPermission>
            </ul>
         </dd>
      </dl>
   </div>
</shiro:hasPermission>

在这里我是以路径作为权限名,也可以使用其他

 

注:每一次遇到权限标签就会触发授权,到数据库中查询。如果权限标签比较多,请使用缓存,提高效率

 

 

以下两种权限控制未测试

1.6.3             第三种 注解方式实现权限控制

Shiro为我们提供了5中注解

         @RequiresAuthentication:验证用户是否登录,等同于方法subject.isAuthenticated()结果为true时。

         @RequiresUser:验证用户是否被记忆,user有两种含义:一种是成功登录的(subject.isAuthenticated()结果为true); 另外一种是被记忆的(subject.isRemembered()结果为真)。

         @RequiresGuest:验证用户是否是一个guest的请求,与@RequiresUser完全相反。不需要经过认证

         @RequiresRoles:例如:@RequiresRoles(“RoleName”)如果subject中有RoleName角色才可以访问方法。如果没有这个权限则抛出异常AuthorizationException

         @RequiresPermissions:例如:@RequiresPermissions(“permissionName1”,“permissionName2”)要求subject中必须同时含有permissionName1permissionName2权限才能执行方法,否则抛出异常AuthorizationException

在spring-mvc.xml配置文件中配置(要写在最先加载的xml中)

<!--权限注解开启-->
<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>
lifecycleBeanPostProcessor和securityManager是在shiro配置文件中定义好的:
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>

<!-- Shiro安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="jdbcRealm"></property>
        <property name="cacheManager" ref="cacheManager"></property>
    </bean>

a)    业务层注解权限控制

注意事项1

因为我们是在实现类上去使用的注解,需要我们指定代码方式


注意事项2

在service层因为权限出现问题,那么会产生异常,我们需要在表现层进行处理

在抛出异常上,可采用springMVC的统一异常处理:点击打开链接

1.6.4             第四种 代码级别权限控制

Subject subject = SecurityUtils.getSubject();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值