(转)Shiro学习

(二期)13、权限框架shiro讲解

【课程13】自定义Realm.xmind36.8KB

【课程13】用户授权流程.xmind0.2MB

【课程13】shiro简介.xmind0.3MB

【课程13】拓展知...登录.xmind15.4KB

【课程13】renren_f...集成.xmind0.3MB

【课程13】用户认证流程.xmind0.2MB

【课程13预习】shir...概念.xmind59.9KB

 

简介

官方源码:https://github.com/apache/shiro

 

在Web系统中我们经常要涉及到权限问题,例如不同角色的人登录系统,他操作的功能、按钮、菜单是各不相同的,这就是所谓的权限。

 

而构建一个互联网应用,权限校验管理是很重要的安全措施,这其中主要包含:

  • 用户认证 - 用户身份识别,即登录
  • 用户授权 - 访问控制
  • 密码加密 - 加密敏感数据防止被偷窥
  • 会话管理 - 与用户相关的时间敏感的状态信息

Shiro对以上功能都进行了很好的支持,它可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。这不就是我们想要的嘛,而且Shiro的API也是非常简单。

组件结构
外部架构

Shiro有三个主要的概念,Subject、SecurityManager 和Realms,下图是这三个组件的交互关系。

Subject:就是与系统交互的当前”用户”,用户不仅仅是人,也可以是第三方服务,爬虫等正在与系统交互的任何事物。

Subject currentUser = SecurityUtils.getSubject();

在获取了Subject对象之后,就可以执行包括登录、登出、获取会话、权限校验等操作。Shiro的简单易用的API,使得我们在程序的任何地方都能很方便地获取当前登录用户,并进行登录用户的各项基本操作。

 

SecurityManager:是Shiro架构的核心,协调内部各个安全组件之间的交互,通常情况下,一旦SecurityManager和它的内部各个组件被配置好之后就不会再用到,开发者通常是查看Subject 的API。

当我们和Subject交互的时候,实际上是SecurityManager在背后协调跟Subject安全相关的操作。

SecurityManager则管理所有用户的安全操作,它是Shiro框架的核心。一旦其初始化配置完成,我们就不会再调用其相关API了,而是将精力集中在了Subject相关的权限操作上了。

 

Realms:在Shiro和用户的应用程序之间扮演着桥梁和连接器的作用。当需要验证或者授权的时候,Shiro从一个或者多个配置的Realms中查找。

这种情况下,Realm是一个安全的DAO,它封装了具体数据库连接的细节,当Shiro需要的时候为Shiro提供需要的数据。当配置Shiro的时候,必须配置至少一个Realm来验证和授权。SecurityManager 可以配置多个Realm,至少需要一个。

Shiro提供了即用的Realm用来连接到各种安全的数据源,像LDAP, 关系型数据库(JDBC), 文本配置的INI和properties文件等。 用户可以插入自己的Realm 实现,如果默认的Realm 不能满足需求的话。

 

  • 记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
详细架构

 

 

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

 

SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。

 

Authenticator认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

 

Authorizer授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

 

Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;

 

Subject及Realm,分别是主体及验证主体的数据源。

 

Session:Shiro提供一个权限的企业级Session解决方案,可以运行在简单的命令行或者是智能手机平台上,也可以工作在大型的集群应用上。

以往我们需要使用Session的一些特性支持时,往往只能将服务部署在web容器或者EJB的Session特性。

Shiro的Session管理方案比上述两种方案都更简单,而且他可以运行在任何应用中,与容器无关。

在Shiro中,session的生命周期都在SessionManager中进行管理

 

SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);

 

SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

 

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

 

Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

 

The SecurityManager

SecurityManager 执行安全操作,管理用户的状态,Shiro的默认SecurityManager 实现包括以下部分

  • Authentication
  • Authorization
  • Session Management
  • Cache Management
  • Realm coordination
  • Event propagation
  • “Remember Me”
  • Services
  • Subject creation
  • Logout 
  • and more.

为了简化配置并且使应用灵活,Shiro的实现都是高度模块化设计的。 

SecurityManager 通常扮演一个轻量的容器,代表其他组件,这个装饰模式的设计可以通过上面的架构图看出。

其他组件各司其职,而SecurityManager 负责协调各个组件。

一个简单例子

官网例子:http://shiro.apache.org/tutorial.html

官网git:https://github.com/apache/shiro/tree/master/samples/quickstart

 

在应用中使用Shiro,我们首先要明白的是在Shiro中的所有组件都和一个核心组件相关,这个组件就是SecurityManager。

 

我们将会在Shiro架构章节详细讲述Shiro的设计细节,现在我们只要知道Shiro SecurityManager是所有使用Shiro的应用的核心,并且每个应用都需要一个SecurityManager就已经足够了。因此,第一件事就是我们必须在我们的应用中获取一个SecurityManager实例。

关于subject

在独立应用中调用getSubject()可以从指定位置的用户数据返回一个Subject,在服务器环境下(例如 web app),将基于当前线程或收到的请求返回一个Subject。

关于session

Session是一个特定的Shiro实例,它不仅代表最常用的普通HttpSesstion,而且还带有一些额外的东西,其中最大的区别是:Shiro Session并不需要HTTP环境!

 

如果在web应用中使用,默认情况下Session就是HttpSession。但是在非web环境下,例如在我们教程中创建的这个应用里,Shiro将自动默认使用企业级会话管理。也就是说在你的应用中你可以使用同一套API而不用关心你应用的发布环境。这就为那些需要使用会话却不想强制使用HttpSession或则EJB会话的应用打开了一个新的世界。而且,客户端还可以分享会话数据。

关于SecurityManager

通过ini的方式可以配置SecurityManager,里面包含用户信息、角色、权限、url权限信息。SecurityManager通常是单例的。

SecurityManager则管理所有用户的安全操作,它是Shiro框架的核心。一旦其初始化配置完成,我们就不会再调用其相关API了,而是将精力集中在了Subject相关的权限操作上了。

常用API
#获取当前用户
Subject currentUser = SecurityUtils.getSubject(); 
#判断用户已经认证
currentUser.isAuthenticated() 
#用户登录
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); 
#记住我
token.setRememberMe(true); currentUser.login(token); 
#判断是否有角色权限
currentUser.hasRole("schwartz") 
#判断是否有资源操作权限
currentUser.isPermitted("lightsaber:wield") 
#登出
currentUser.logout();

拓展:

  • 注解判断是否有权限,通过spring aop的形式完成
//属于user角色
@RequiresRoles("user")
//必须同时属于user和admin角色
@RequiresRoles({"user","admin"})
//属于user或者admin之一;修改logical为OR 即可
@RequiresRoles(value={"user","admin"},logical=Logical.OR)

//符合index:hello权限要求 
@RequiresPermissions("index:hello")
//必须同时复核index:hello和index:world权限要求 
@RequiresPermissions({"index:hello","index:world"})
//符合index:hello或index:world权限要求即可 
@RequiresPermissions(value={"index:hello","index:world"},logical=Logical.OR)

@RequiresAuthentication
@RequiresUser
@RequiresGusst
步骤解析
认证流程

  • 1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;

  • 2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;

  • 3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;

  • 4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
  • 5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

使用UsernamePasswordToken,支持最常见的用户名/口令的认证方式。它是org.apache.shiro.authc.AuthenticationToken接口的一个实现

 

常见异常

  • DisabledAccountException(禁用的帐号)
  • LockedAccountException(锁定的帐号)
  • UnknownAccountException(错误的帐号)
  • ExcessiveAttemptsException(登录失败次数过多)
  • IncorrectCredentialsException (错误的凭证)
  • ExpiredCredentialsException(过期的凭证)

 

认证流程自定义的功能

  • Realms - 认证授权信息来源
  • JdbcRealm
  • IniRealm
  • 自定义Realm,编写用户登录认证过程
  • SessionDAO - 会话的管理
  • 把session信息存到redis、memcache等缓存中间件中
  • 其中,session的id可以自定义生成格式:属性:sessionIdGenerator
  • AuthenticationToken - 用户Subject提交的有关登录主体和凭证的基本信息组合
  • 默认方式UsernamePasswordToken,即密码登录模式
  • 通过implement AuthenticationToken的方式来实现自定义的登录方式和特殊的必需登录数据的索取
  • 如:jwt方式的一般需要实现AuthenticationToken接口添加上token字段信息

 

授权流程

授权(Authorization)也叫做访问控制,是一个对资源的访问进行管理的过程,也就是说在应用程序汇总,谁有怎样的权限(用户可以看到什么内容,可以进行什么操作)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

 

授权的三要素

 

主体

主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

资源

在应用中用户可以访问的任何东西,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

权限

安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:

访问用户列表页面

查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)

打印文档等等。。。

角色

角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

  • 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表示授权失败

 

ModularRealmAuthorizer进行多Realm匹配流程:

1、首先检查相应的Realm是否实现了实现了Authorizer;

2、如果实现了Authorizer,那么接着调用其相应的isPermitted*/hasRole*接口进行匹配;

3、如果有一个Realm匹配那么将返回true,否则返回false。

 

Realm授权

1、如果Realm进行授权的话,应该继承AuthorizingRealm,其流程是:

(1)如果调用hasRole*,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;

(2)首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;

2、通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合;通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例;然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供);

3、接着调用Permission. implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false.

 

Shiro支持三种方式实现授权过程: 

  • 编码实现
Subject subject = SecurityUtils.getSubject();  
if(subject.hasRole(“admin”)) {  
    //有权限  
} else {  
    //无权限  
}
  • 注解实现
@RequiresRoles("admin")  
public void hello() {  
    //有权限  
}  
  • JSP Taglig实现
<shiro:hasRole name="admin">  
<!— 有权限 —>  
</shiro:hasRole>  

jsp页面引入shiro标签

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
自定义Realm

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。如我们之前的ini配置方式将使用org.apache.shiro.realm.text.IniRealm。

 

  • AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token):

表示获取身份验证信息;

 

  • AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals):

表示根据用户身份获取授权信息。

 

在线会话管理
获取当前会话总人数
@Autowired
private SessionDAO sessionDAO;

//获取会话数量
int size = sessionDAO.getActiveSessions().size()
强制下线
//强制退出
Session session = sessionDAO.readSession(subject.getSession().getId());
sessionDAO.delete(session);

// logout,可作为强制退出
subject.logout();
Assert.isTrue(!subject.isAuthenticated());
springboot集成shiro

第一步,导入pom坐标文件

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.4.0</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.0</version>
</dependency>

 

第二步:继承AuthorizingRealm,重写授权和认证方法。

@Component
public class OAuth2Realm extends AuthorizingRealm{

  //授权
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
  }
  
  //认证
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
  }
}

第三步:自定义shiro过滤器,自定重写isAccessAllowed、onAccessDenied、onLoginFailure等方法

public class OAuth2Filter extends AuthenticatingFilter {
  
  
}

第四步:定义shiro配置。注意自定义Bean。securityManager、和shiroFilter。

@Configuration
public class ShiroConfig {

  @Bean("securityManager")
  public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager){
      DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  securityManager.setRealm(oAuth2Realm);
  
  return securityManager;

  }

  @Bean("shiroFilter")
  public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    
  }
}
 

第五步:在需要权限控制的地方,使用注解。

  • @RequiresPermissions
  • @RequiresRoles
renren-fast的shiro解析

1、sys_user[用户]表,保存用户相关数据,通过sys_user_role[用户与角色关联]表,与

sys_role[角色]表关联;sys_menu[菜单]表通过sys_role_menu[菜单与角色关联]表,与

sys_role[角色]表关联

 

2、 sys_menu表,保存菜单相关数据,并在perms字段里,保存了shiro的权限标识,也就是

说,拥有此菜单,就拥有perms字段里的所有权限,比如,某用户拥有的菜单权限标

识 sys:user:info ,就可以访问下面的方法

 

3 . 在shiro配置代码里,配置为 anon 的,表示不经过shiro处理,配置为 oauth2 的,表示经过 OAuth2Filter 处理,前后端分离的接口,都会交给 OAuth2Filter 处理,这样就保证,没有权限的请求,拒绝访问。

 

 

 

单点登录

什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分

1、登录

相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。

 

这个过程,也就是单点登录的原理,用下图说明

 

下面对上图简要描述

  1. 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  1. sso认证中心发现用户未登录,将用户引导至登录页面
  1. 用户输入用户名密码提交登录申请
  1. sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
  1. sso认证中心带着令牌跳转会最初的请求地址(系统1)
  1. 系统1拿到令牌,去sso认证中心校验令牌是否有效
  1. sso认证中心校验令牌,返回有效,注册系统1
  1. 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
  1. 用户访问系统2的受保护资源
  1. 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  1. sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
  1. 系统2拿到令牌,去sso认证中心校验令牌是否有效
  1. sso认证中心校验令牌,返回有效,注册系统2
  1. 系统2使用该令牌创建与用户的局部会话,返回受保护资源

用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话,用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心,全局会话与局部会话有如下约束关系

  1. 局部会话存在,全局会话一定存在
  1. 全局会话存在,局部会话不一定存在
  1. 全局会话销毁,局部会话必须销毁

你可以通过博客园、百度、csdn、淘宝等网站的登录过程加深对单点登录的理解,注意观察登录过程中的跳转url与参数

 

2、注销

单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明

 

sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作。

 

下面对上图简要说明

  1. 用户向系统1发起注销请求
  1. 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
  1. sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
  1. sso认证中心向所有注册系统发起注销请求
  1. 各注册系统接收sso认证中心的注销请求,销毁局部会话
  1. sso认证中心引导用户至登录页面

sso文章:https://www.cnblogs.com/ywlaker/p/6113927.html

sso代码:https://github.com/sheefee/simple-sso

 

posted @ 2018-10-24 09:31  free_wings  阅读(348)  评论(0编辑  收藏  举报