shiro 的学习笔记

shiro是Java的一个安全权限框架

主要四大功能
authentication authorization session mangement cryptography
认证 授权 会话管理 加密

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用
户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信
息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到 Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过
相应的接口注入给 Shiro 即可。

application - >subject  ->shrio securitymanager -> realm
应用                当前用户       验证                                接受用户信息

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager : 相 当 于 SpringMVC 中 的 DispatcherServlet 或 者 Struts2 中的
FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
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 提高了一些常见的加密组件用于如密码加密


shiroFilter  拦截器

spring集成时,web.xml配置拦截器DelegatingFilterProxy 的名字和IOC容器shiroFilter的id一致
DelegatingFilterProxy是shiroFilter 的代理,以上情况是默认情况。
可以用配置初始化参数指定IOC容器中shiroFilter 拦截器的id
<param-name>targetBeanName</>
<param-value>id</>

shiroFilter  拦截器的属性
filterChainDefinitions属性
url=拦截器[参数]       anon匿名  authc有权限 logout退出登录 roles角色 roles[权限]  user
url支持ANT风格 * ** ?
url权限第一次优先匹配的方式   

Shiro 身份验证
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。
credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

最常见的 principals 和 credentials 组合就是用户名 / 密码了。

=========================
shiro.ini 配置文件

[users]
zhang=123
wang=123

此处使用 ini 配置文件,通过 [users] 指定了两个主体:zhang/123、wang/123。

测试用例

@Test
public void testHelloworld() {
    //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager 
     Factory<org.apache.shiro.mgt.SecurityManager> factory =
            new IniSecurityManagerFactory("classpath:shiro.ini");

    //2、得到SecurityManager实例 并绑定给SecurityUtils   
    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);


    //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
    try {
        //4、登录,即身份验证
        subject.login(token);
    } catch (AuthenticationException e) {
        //5、身份验证失败
    }
    Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
    //6、退出
    subject.logout();
}


普通认证流程
1 获取当前的subject,调用securetyUtils.getSubject()
2 测试当前用户是否以及被认证了,即是否登录了,调用subject.isAuthenticated()
3 若没有被认证,获取表单数据,则把用户名和密码封装成usernamePasswordToken对象
4 执行登录 ,调用subject.login(AuthenticationToken)方法,把封装的token对象传入该方法
5 自定义realm方法,从数据库中获取用户名的信息,返回给shiro
6 shiro进行密码比对。


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

重要的一点是:realm需要交给securityManager管理,这样才能当用户调用subject.login()方法时,调用我们设置的realm,可以用代码设置,也可以在容器里配置
securityManager.setRealms(new MyRealm()); 多个realm对象时,把这些对象打包成一个list,再传入。


自定义realm
org.apache.shiro.realm.AuthencatingReam类
实现doGetAuthenticationInfo(AuthenticationToken)方法,这个方法中的token对象即为subject.login(AuthenticationToken)传入的token对象。

doGetAuthenticationInfo方法需要做的事
1 AuthenticationToken对象转换成UsernamePasswordToken
2 UsernamePasswordToken获取username
3 调用数据库的方法,查询数据库username的用户信息
4 若用户不存在,被锁定,抛出相应的异常
5 根据用户信息的情况,抛出其他异常AuthenticationException
6 根据用户的情况,构建AuthenticationInfo对象,并返回给方法


@Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();  //得到用户名
        String password = new String((char[])token.getCredentials()); //得到密码
        if(!"zhang".equals(username)) {
            throw new UnknownAccountException(); //如果用户名错误
        }
        if(!"123".equals(password)) {
            throw new IncorrectCredentialsException(); //如果密码错误
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo实现;
        return new SimpleAuthenticationInfo(username, password, getName());
    }


AuthenticationInfo  通常使用实现类SimpleAuthenticationInfo 
principal  认证的实体信息
credentials 密码  以上两个信息是从数据库获取
realmName   =getName()调用父类getName的获取
SimpleAuthenticationInfo( principal credentials credentialsSalt=null realmName)

credentialsSalt=ByteSource.Util.bytes(username)
SimpleHash(hashAlgorithName creden.. salt hashIterations)hashAlgorithName加密算法的名字 hashIterations加密次数
salt盐值 密码相同时,区分用户的手段,以上方法可以返回加密密码返回的结果哈希值
要求盐值唯一,一采用用户名或者随机的字符串

密码加密 为了不让密码以明文的方式展现
密码的比对  AuthenticatingRealm 的credentialsMatcher属性类进行密码匹配

AuthenticatingRealm 类的属性credentialsMatcher可以赋予不同的实现类,采用不同的加密算法。
HashedCredentialsMatcher实现类 具有hashAlgorithName=MD5  hashIterations=1024

myRealm.sethashAlgorithName()  为credentialsMatcher属性设置加密算法
myRealm.sethashIterations()    为credentialsMatcher属性设置加密次数

SimpleHash()这个方法可以算出密码加密后的结果。


多各数据库,即多个realm的情况下
Authenticator 认证器,管理realms的类
ModularRealmAuthenticatior类是认证器的一个实现类
ModularRealmAuthenticatior类的realms属性 是列表类型,可以装入多个realm,进行管理。
ModularRealmAuthenticatior 具有AuthenticationStrategy属性,这个属性很重要,如果你不想使用默认的认证策略,你可以生成一个认证类,用setter
更改认证策略,再交给securityManager处理,期间你可以用认证器realms属性添加全部的realm,就无需向securityManager设置realms源。

realms属性列表的realm顺序即为认证的顺序

当有多个realm的情况下又采用认证策略
AuthenticationStrategy
AuthenticationStrategy接口的默认实现类有
FirstSuccessfulStrategy  只要有一个realm认证成功即可,返回第一个realm认证成功的信息
AtLeastOneSuccessfulStrategy只要有一个realm认证成功即可,返回所有realm的认证信息   默认的认证策略
AllSuccessfulStrategy 所有的realm验证成功才返回信息,任何一个失败认证失败

授权
Authrizer:授权器
需要把realm 传给SecurityManager

也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)


判断用户是否有权限,两种方式

#1 if(subject.hasRole(“admin”)) {
    //有权限
} else {
    //无权限
}

#2 @RequiresRoles("admin")
public void hello() {
    //有权限
}

==================
最重要的一点是,需要在shiro的配置文件设置用户用于的权限或者角色。

而与Web集成时,只需要在shiroFilter设置页面的访问权限,又在配置文件设置用户的权限,用户进入相应的页面后,shiro自动调用过滤器判断
用户是否有权限,无需显示地调用代码判断用户权限。

自定义

权限
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面
查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)
打印文档等等。。。

shiroFilter属性unauthorizedUrl 指定没有权限去的页面


subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
@RequiresRoles(“admin”) :在方法上加注解的时候,或者xml中配置了某个请求需要什么权限的时候;
将会执行

AuthrizingRealm类

doGetAuthorizationInfo(PrincipalCollection )
1 从PrincipalCollection获取登录用户的信息
2 利用登录用户的信息,从数据库中查找该用户是否具有相应的角色或权限
3 创建SimpleAuthorizationInfo,并设置roles属性。 SimpleAuthorizationInfo(roles)
4 返回Info

roles实现

roles[system,general] ,表示同时需要“system”和“general” 2个角色才通过认证

给shiroFilter的filterChainDefinitions属性设置页面的访问权限  而unauthorizedUrl属性 :没有权限默认跳转的页面
successUrl属性 :登录成功默认跳转页面,不配置则跳转 至”/”  loginUrl属性 :没有登录的用户请求需要登录的页面时 自动跳转到登录页面,不是必须的属性
<value>  
                /login = authc       
                /login/logout = anon  匿名
                / = anon  
                /XXX/** = user,anyRoles[system,general]  
                                /TTT = role[system]  
                /** = user  
 </value>  


filter的过滤器有 anno authc需要登录 authcBasic perms roles user logout

注:anon,authcBasic,auchc,user是认证过滤器,

perms,roles,ssl,rest,port是授权过滤器

user:例如/admins/user/**=user没有参数表示必须存在用户, 当登入操作时不做检查

perms:例子/admins/user/**=perms[user:add:*],参数可以写 多个,多个时必须加上引号,并且参数之间用逗号分割, 例如/admins/user/**=perms["user:add:*,user:modify:*"],当 有多个参数时必须每个参数都通过才通过,想当于 isPermitedAll()方法。

权限注解
@RequiresAuthentication  当前的用户subject已经通过login验证,subject.isAuthenticated()返回true

@RequiresUser  表示当前用户已经经过身份验证 或通过记录我登录

@RequiresGuest 表示当前用户没有经过身份验证 和没通过记录我登录,为游客身份

@RequiresRoles(value={'a','b'} ,logical=Logical.AND)表示当前用户同时需要a,b角色

@RequiresPermissions(value={'user:a' ,'user:b'},logical=Logical.OR)
表示当前用户需要a 权限或者b权限

shiro从数据库中获取初始资源,即页面的权限信息等。
shiroFilter 的filterChainDefinitionMap属性
配置一个bean,实际上是一个对象,通过实例工厂方法的方式factory-method指定的bean方法返回map对象
map.put('/url','anon')  LinkedHashMap必须的,添加要有顺序


会话管理
session  与serlvet的session没有多大的区别
sessionListener提供的session监听器

SecurityUtils.getSubject().getSession()

service层也可以访问session里面的数据

SessionDao接口
EnterpriseCacheSessionDao实现类  开发时继承该Dao
MemorySessionDao实现类 内存当中操作session

sessionIdGenerator
EnterpriseCacheSessionDao
属性sessionIdGenerator  =要配置sessionID 必须的
会话管理器
sessionManager
EnterpriseCacheSessionDao实现类要配置给会话管理器,而会话管理器要配置给Security

sessionDao对session进行增删改查,利用对象输入输出流

会话验证 验证会话是否过期,过期停止会话
在web环境,shiro 提供会话验证调度器,定期验证会话是否过期
SessionValidationScheduler
提供了一个Quartz调度器QuartzSessionValidationScheduler

缓存
shiro内部相应的组件DefaultSecurityManager会自动检测相应的对象,如realm
是否实现了CacheManagerAware接口,并自动注入相应的CacheManager中。

RemenberMe 记住我
把cookie写到客户端
subject.isRemenberd()
认证和记住我只能二选其一

user过滤器,可以通过记住我的方式方面页面
token.setRemenberMe(true)

securityManager的remenberMeManager属性    设置cookie过期时间remenberMeManager.cookie.Maxage
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值