Apache Shiro入门

朴灿烈づ我的快乐病毒、 2022-07-13 11:25 326阅读 0赞

Apache Shiro入门

@(Shiro)[shiro,安全框架]

  • Apache Shiro入门

    • Apache Shiro基本概述

      • Apache Shiro基本概念

        • 使用Shiro能做什么
        • Shiro的特性
      • Shiro框架的调用流程
      • Shiro框架
    • 快速入门案例

      • 引入Maven依赖
      • 在webxml中配置spring框架提供的过滤器用于整合shiro框架
      • 在Spring配置文件中配置bean

        • 配置安全管理器对象shiro框架最核心的对象
        • 配置Shiro提供的一个工厂对象用于产生过滤器对象
      • 自定义一个Realm类
      • 将该Realm配置到安全管理器中
      • 写一个登录方法
      • 补充默认拦截器
    • 权限控制的四种方式

      • 基于URL的权限控制
      • 基于注解的权限控制

        • 在Spring配置文件中启动注解支持
        • 在Action的方法上使用shiro注解进行权限控制
      • 基于JSTL标签库的权限控制

        • 在jsp页面中引入shiro的标签库
        • 使用shiro提供的标签进行权限控制
      • 基于编程的权限控制
    • 使用ehcache缓存权限数据

      • 引入ehcache的依赖
      • 引入ehcache配置文件
      • 在spring配置文件中配置缓存管理器对象并注入给安全管理器

Apache Shiro基本概述

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

——参考《百度百科》

官网:Apache Shiro官网

Apache Shiro基本概念

使用Shiro能做什么

  • 验证用户(认证)。
  • 对用户执行访问控制,像是:
  1. * 判断用户是否具有某一角色。
  2. * 判断用户是否具有做某事的权限。
  • 在任何环境下都能使用Session API,即使没有诸如Spring,EJB这样的容器。
  • 在授权,访问控制或者会话生命周期中,都能响应事件。
  • 可以使用多个数据源。
  • 支持单点登录(SSO)功能。
  • 支持”Remember Me”服务。

Shiro的特性

这里写图片描述

从上图可以看出,shiro有4个主要特性,分别是认证(Authentication)、授权(Authorization)、会话管理(Session Management)和密码(Cryptography)服务。

以及6个附加(支持)特性,分别是Web支持(Web Support)、缓存(Caching)、并发(Concurrency)、测试(Testing)支持、以…运行(Run As)、记住我(Remember Me)。

参照开涛的解释,如下:

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

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

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

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

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

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

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

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

Shiro框架的调用流程

这里写图片描述

Shiro框架有三个核心组件,分别是:Subject, SecurityManagerRealms.

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

Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

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

从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

Shiro框架

这里写图片描述

快速入门案例

引入Maven依赖

  1. <!-- 引入shiro框架的依赖 -->
  2. <dependency>
  3. <groupId>org.apache.shiro</groupId>
  4. <artifactId>shiro-all</artifactId>
  5. <version>1.2.2</version>
  6. </dependency>

在web.xml中配置spring框架提供的过滤器,用于整合shiro框架

  1. <!-- 配置Spring整合shiro框架的过滤器,过滤器必须配置在Struts2核心过滤器上面 -->
  2. <filter>
  3. <filter-name>shiroFilter</filter-name>
  4. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  5. </filter>
  6. <filter-mapping>
  7. <filter-name>shiroFilter</filter-name>
  8. <url-pattern>/*</url-pattern>
  9. </filter-mapping>

在Spring配置文件中配置bean

配置安全管理器对象(shiro框架最核心的对象)

  1. <!-- 配置安全管理器对象 -->
  2. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  3. </bean>

配置Shiro提供的一个工厂对象,用于产生过滤器对象

  1. <!-- 配置Shiro提供的一个工厂对象,用于产生过滤器对象 -->
  2. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  3. <!-- 配置相关URL -->
  4. <!-- 配置登录URL -->
  5. <property name="loginUrl" value="/login.jsp"/>
  6. <!-- 配置登录成功URL -->
  7. <property name="successUrl" value="/home.jsp"/>
  8. <!-- 配置未授权URL -->
  9. <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
  10. <!-- 配置URL拦截规则 -->
  11. <property name="filterChainDefinitions">
  12. <value>
  13. /css/** = anon
  14. /js/** = anon
  15. /images/** = anon
  16. /login.jsp* = anon
  17. /userAction_login* = anon
  18. /manager* = perms["manager"]
  19. /* = authc
  20. </value>
  21. </property>
  22. <property name="securityManager" ref="securityManager"/>
  23. </bean>

PS:Bean的ID要和过滤器中名称一致,因为过滤器在初始化时,就会加载与其名称一致的Bean。

自定义一个Realm类

  1. package com.service.realm;
  2. import java.util.List;
  3. import javax.annotation.Resource;
  4. import org.apache.shiro.authc.AuthenticationException;
  5. import org.apache.shiro.authc.AuthenticationInfo;
  6. import org.apache.shiro.authc.AuthenticationToken;
  7. import org.apache.shiro.authc.SimpleAuthenticationInfo;
  8. import org.apache.shiro.authc.UsernamePasswordToken;
  9. import org.apache.shiro.authz.AuthorizationInfo;
  10. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  11. import org.apache.shiro.realm.AuthorizingRealm;
  12. import org.apache.shiro.subject.PrincipalCollection;
  13. import org.hibernate.criterion.DetachedCriteria;
  14. import org.hibernate.criterion.Restrictions;
  15. import com.dao.FunctionDao;
  16. import com.dao.UserDao;
  17. import com.domain.Function;
  18. import com.domain.User;
  19. /** * 用户Realm * * @author Switch * @data 2016年12月26日 * @version V1.0 */
  20. public class UserRealm extends AuthorizingRealm {
  21. // 注入用户Dao对象
  22. @Resource(name = "userDao")
  23. private UserDao userDao;
  24. // 注入权限Dao对象
  25. @Resource(name = "functionDao")
  26. private FunctionDao functionDao;
  27. // 授予权限
  28. @Override
  29. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  30. // 创建简单授权对象
  31. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  32. // 为用户授权
  33. // 权限列表
  34. List<Function> functions = null;
  35. // 获取当前用户
  36. User user = (User) principals.getPrimaryPrincipal();
  37. if ("admin".equals(user.getUsername())) {
  38. // 超级管理员,获取所有权限
  39. functions = functionDao.findAll();
  40. } else {
  41. // 普通用户,通过用户id获取其对应的权限
  42. functions = functionDao.findByUserId(user.getId());
  43. }
  44. if (functions != null && functions.size() > 0) {
  45. for (Function function : functions) {
  46. // 添加许可
  47. info.addStringPermission(function.getCode());
  48. }
  49. }
  50. return info;
  51. }
  52. // 权限认证
  53. @Override
  54. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  55. // 强转为子类,用户名密码token,获取Subject传递过来的数据
  56. UsernamePasswordToken userToken = (UsernamePasswordToken) token;
  57. // 获取用户名
  58. String username = userToken.getUsername();
  59. // 在数据库中查询客户
  60. DetachedCriteria detachedCriteria = DetachedCriteria.forClass(User.class);
  61. detachedCriteria.add(Restrictions.eq("username", username));
  62. List<User> userList = userDao.findByCriteria(detachedCriteria);
  63. if (userList != null && userList.size() > 0) {
  64. User user = userList.get(0);
  65. // 获取密码
  66. String password = user.getPassword();
  67. // 简单认证信息对象,由SecurityManager负责对比密码数据
  68. // 参数1:Object principal————一般是认证对象,在这里就是用户对象
  69. // 参数2:Object credentials————证书,也就是负责校验认证的对象
  70. // 参数3:String realmName————realm的名字,随意,但必须唯一
  71. AuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());
  72. return info;
  73. }
  74. return null;
  75. }
  76. }

将该Realm配置到安全管理器中

  1. <!-- 配置用户Realm Bean -->
  2. <bean id="userRealm" class="com.itheima.bos.service.realm.UserRealm"/>
  3. <!-- 配置安全管理器对象 -->
  4. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  5. <!-- 管理Realm -->
  6. <property name="realm" ref="userRealm"/>
  7. </bean>

写一个登录方法

  1. public String login() throws Exception {
  2. // 获取session中的验证码
  3. String loginCheckCode = (String) ActionContext.getContext().getSession().get("loginCheckCode");
  4. if (StringUtils.isBlank(checkcode) || !loginCheckCode.equals(checkcode)) {
  5. this.addActionError("验证码错误");
  6. return LOGIN;
  7. }
  8. // Shiro认证
  9. // 获取subject,代表当前用户对象,状态为:未认证
  10. Subject subject = SecurityUtils.getSubject();
  11. AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), MD5Utils.md5(model.getPassword()));
  12. try {
  13. // 验证
  14. subject.login(token);
  15. // 从本地线程中获取SimpleAuthenticationInfo放入的对象
  16. User loginUser = (User) subject.getPrincipal();
  17. // 加入Session
  18. ActionContext.getContext().getSession().put("loginUser", loginUser);
  19. return HOME;
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. this.addActionError("用户名或密码错误!");
  23. return LOGIN;
  24. }
  25. }

补充:默认拦截器

Shiro内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器:

















































































默认拦截器名 拦截器类 说明(括号里的表示默认值)
身份验证相关的
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 基于表单的拦截器;如“/=authc”,如果没有登录会跳到相应的登录页面登录;
主要属性:
usernameParam:表单提交的用户名参数名( username ) ;
passwordParam:表单提交的密码参数名(password);
rememberMeParam:表单提交的密码参数名(rememberMe);
loginUrl:登录页面地址(/login.jsp);
successUrl:登录成功后的默认重定向地址;
failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter Basic HTTP身份验证拦截器,
主要属性:
applicationName:弹出登录框显示的信息(application);
logout org.apache.shiro.web.filter.authc.LogoutFilter 退出拦截器,
主要属性:
redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”
user org.apache.shiro.web.filter.authc.UserFilter 用户拦截器,用户已经身份验证/记住我登录的都可;示例“/=user”
anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤; 示例“/static/=anon”
授权相关的
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 角色授权拦截器,验证用户是否拥有所有角色;
主要属性:
loginUrl:登录页面地址(/login.jsp);
unauthorizedUrl:未授权后重定向的地址; 示例“/admin/=roles[admin]”
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 权限授权拦截器,验证用户是否拥有所有权限; 属性和roles 一样; 示例“/user/**=perms[“user:create”]”
port org.apache.shiro.web.filter.authz.PortFilter 端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80 并重定向到该80 端口,其他路径/参数等都一样
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter rest 风格拦截器,自动根据请求方法构建权限字符串
( GET=read,POST=create,PUT=update,DELETE=delete,
HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;
示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll);
ssl org.apache.shiro.web.filter.authz.SslFilter SSL 拦截器,只有请求协议是https 才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;
其他
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter 不创建会话拦截器, 调用subject.getSession(false)不会有什么问题,但是如果subject.getSession(true)将抛出DisabledSessionException异常;

PS:在配置URL拦截规则的时候,使用了很多默认拦截器。

权限控制的四种方式

在了解权限控制之前,先模拟下授权操作,授予manager的权限。

  1. // 授予权限
  2. @Override
  3. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  4. // 创建简单授权对象
  5. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  6. // 为用户授权
  7. // TODO : 这里只是模拟授权,一般还需要到数据库中认证
  8. info.addStringPermission("manager");
  9. return info;
  10. }

基于URL的权限控制

  1. <!-- 配置URL拦截规则 -->
  2. <property name="filterChainDefinitions">
  3. <value>
  4. /css/** = anon
  5. /js/** = anon
  6. /images/** = anon
  7. /login.jsp* = anon
  8. /userAction_login* = anon
  9. /manager* = perms["manager:*"]
  10. /* = authc
  11. </value>
  12. </property>

基于注解的权限控制

在Spring配置文件中启动注解支持

  1. <aop:config proxy-target-class="true"/>
  2. <!-- 自动代理 -->
  3. <bean id="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
  4. <!-- 强制使用cglib进行代理 -->
  5. <property name="proxyTargetClass" value="true"/>
  6. </bean>
  7. <!-- 配置注解方式权限控制的切面类 -->
  8. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" />

在Action的方法上使用shiro注解进行权限控制

  1. //调用该方法,需要manager:delete权限
  2. @RequiresPermissions("manager:delete")
  3. public void deleteIds() throws Exception {
  4. }

基于JSTL标签库的权限控制

在jsp页面中引入shiro的标签库

  1. <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

使用shiro提供的标签进行权限控制

  1. <%-- 拥有manager:view才会显示 --%>
  2. <shiro:hasPermission name="manager:view">
  3. <input type="text" name="name"/>
  4. </shiro:hasPermission>

基于编程的权限控制

  1. public void edit() throws Exception {
  2. // 编程实现权限控制
  3. // 调用Shiro框架提供的工具类,获取本地线程上的Subject对象
  4. Subject subject = SecurityUtils.getSubject();
  5. // 检测权限,无则抛出异常
  6. subject.checkPermission("manager:update");
  7. }

PS:当尚未授予manager权限时,会抛出org.apache.shiro.authz.UnauthorizedException这个异常,所以注意捕获并处理这个异常。如果使用Struts2框架,可以采取如下方式捕获并处理:

  1. <package name="basePackage" extends="struts-default" abstract="true">
  2. <!-- 全局视图 -->
  3. <global-results>
  4. <result name="login">/login.jsp</result>
  5. <result name="Unauthorized">/unauthorized.jsp</result>
  6. </global-results>
  7. <!-- 全局异常捕获 -->
  8. <global-exception-mappings>
  9. <exception-mapping result="Unauthorized" exception="org.apache.shiro.authz.UnauthorizedException"/>
  10. </global-exception-mappings>
  11. </package>

使用ehcache缓存权限数据

引入ehcache的依赖

  1. <!-- 引入ehcache的依赖 -->
  2. <dependency>
  3. <groupId>net.sf.ehcache</groupId>
  4. <artifactId>ehcache-core</artifactId>
  5. <version>2.6.6</version>
  6. </dependency>

引入ehcache配置文件

  1. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  2. <diskStore path="java.io.tmpdir"/>
  3. <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
  4. <persistence strategy="localTempSwap"/>
  5. </defaultCache>
  6. </ehcache>

PS:将配置文件ehcache.xml放在类根目录下。

在spring配置文件中配置缓存管理器对象,并注入给安全管理器

  1. <!-- 配置安全管理器对象 -->
  2. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  3. <!-- 管理Realm -->
  4. <property name="realm" ref="userRealm"/>
  5. <!-- 注入缓存管理器 -->
  6. <property name="cacheManager" ref="cacheManager"/>
  7. </bean>
  8. <!-- 注册缓存管理器 -->
  9. <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
  10. <!-- 注入ehcache配置文件 -->
  11. <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
  12. </bean>

———–参考《张开涛,跟我学Shiro》
———–参考《官网文档》

发表评论

表情:
评论列表 (有 0 条评论,326人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Apache Shiro 认证

    认证就是验证用户身份的过程。也就是说用户在使用应用时,需要证明他们自己就是所说的那个人。在认证过程中,用户需要提供一些身份识别信息以检验用户是否合法。 这里一般是提交用户身份

    相关 Apache Shiro 认证

    认证就是验证用户身份的过程。也就是说用户在使用应用时,需要证明他们自己就是所说的那个人。在认证过程中,用户需要提供一些身份识别信息以检验用户是否合法。 这里一般是提交用户身份