shiro笔记

# 1.在 shiro 中的概念 #

用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份
Subject 及 Realm,分别是主体及验证主体的数据源

认证[身份验证]: 验证在应用中是否是他本人.
授权[访问控制]: 控制谁能访问哪些资源

主体, 即访问应用的用户,Subject

资源, 在应用中用户可以访问的任何东西,用户授权之后才能访问.

权限, 安全策略的原子授权单位, 表示在应用中用户有没有操作某个资源的权力。

角色, 角色代表权限的集合.为了赋予用户权限时方便, 一般赋予用户多个角色。


 
# 2.身份验证大体流程 #

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,将按照相应的顺序及策略进行访问。

# 3.Authenticator配置 #
Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点

SecurityManager 接口继承了 Authenticator,另外还有一个 **ModularRealmAuthenticator** 实现,其委托给多个 Realm 进行验证,验证规则通过 AuthenticationStrategy 接口指定,默认提供的实现:

FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;

AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;

AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。

***ModularRealmAuthenticator*** 默认使用 ***AtLeastOneSuccessfulStrategy*** 策略。

如果需要自定义的AuthenticationStrategy, 可以继承AbstractAuthenticationStrategy.

# 4.授权方式 #
1. 编程式授权

    Subject subject = SecurityUtils.getSubject();
    if (subject.hasRole("admin")){
        // 有权限
    } else {
        // 无权限
    }

2. 注解式

    通过在执行的JAVA方法上放置相应的注解完成.
    
    @RequiresRoles("admin")
    public void hello(){
        // 有权限才能访问, 无权限将抛出异常.
    }


3. JSP/GSP标签: 在jsp/gsp页面中通过标签完成

    
    
    


# 5.基于资源的访问控制 #
1. 字符串通配符规则

'资源描述符:操作: 对象实例ID' 即对哪个资源的哪个实例可以继续哪些操作. 默认支持通配符权限字符串, ":" 表示资源/操作/实例的分隔;
','表示操作的分隔; "*"表示任意资源/操作/实例

[e.单个资源单个权限]

subject().checkPermissions("system:user:update");  

[e.单个资源全部权限]

"system:user"的'create','update','delete'和'view'所有权限,可以简写成:

    .ini: role52=system:user:* / system:user

然后通过代码判断:
    
    subject().checkPermissions("system:user:*");
    subject().checkPermissions("system:user");

[e.所有资源全部权限]
    .ini: role61=*:view  #所有资源的view权限

    subject().checkPermissions("user:view");

[e.实例级别的权限]
    
    .ini:role71=user:view:1 #对资源user的1实例拥有view权限

    subject().checkPermissions("user:view:1");
[e.单个实例多个权限]

    .ini: role72="user:update,delete:1"
    subject().checkPermissions("user:delete,update:1");
    subject().checkPermissions("user:update:1", "user:delete:1");
    
[e.所有实例 单个权限]
    
    .ini: role74=user:auth:* #对资源user:auth拥有权限
    subject().checkPermmissions("user:auth:1", "user:auth:2");
[e.所有实例所有权限]

    role75=user:*:*  #对资源user的1实例拥有所有权限
    subject().checkPermmissions("user:view:1", "user:auth:2");

权限字符串 缺失部分处理
    
    "user:view"等价于"user:view:*"
    "organization"等价于"organization:*:*"
    *表示匹配所有, 不加*可以进行前缀匹配;

tips:
    
    subject().checkPermission("menu:view:1");
    subject().checkPermission(new WildcardPermission("menu:view:1"));
    这两种是等价的.

# 6.加密与解密 #

## DefaultHashService的使用 ##

设置的散列算法: SHA-512, MD5 等
service可以设置私盐(默认无), 公盐(默认不)
HashRequest 可以设置了公盐
service 如果设置了私盐, service无论如何都会用公盐

返回的Hash对象只可能包含公盐.[私盐在service对象内]

HashRequest里面的配置优先于HashService内的配置.


## 对称式加密与解密 ##

AES, Blowfish等算法

详情见:http://wiki.jikexueyuan.com/project/shiro/encoding.html

## PasswordService/CredentialsMatcher ##

加密密码与密码校验

AuthenticatingRealm默认使用assertCredentialsMatch方法验证token的明文密码和info的密文密码是否一致

而这个方法的默认实现是: 
1. 拿到当前realm的 credentialsMatcher, 用它来验证.

我们可以重写方法,也可以重写设置credentialsMatcher.
下面是重写设置matcher的配置.


    # 这里配置的matcher, 将用于   token里面的明文密码   散列, 和realm返回的info的ps比对
    # 所以, 数据库存储时是如何将密码加密存储的, 这里就如何配置
    credentialsMatcher=com.ren.credentials.RetryLimitHashedCredentialsMatcher
    credentialsMatcher.hashAlgorithmName=md5
    credentialsMatcher.hashIterations=2
    credentialsMatcher.storedCredentialsHexEncoded=true

    userRealm=com.ren.realm.UserRealm
    userRealm.credentialsMatcher=$credentialsMatcher
    securityManager.realms=$userRealm


# 7.Realm #

AuthenticationToken 用于收集用户提交的身份(如用户名)及凭据(如密码)

AuthenticationInfo 有两个作用:

如果 Realm 是 AuthenticatingRealm 子类,则提供给 AuthenticatingRealm 内部使用的 CredentialsMatcher 进行凭据验证;(如果没有继承它需要在自己的 Realm 中自己实现验证);
提供给 SecurityManager 来创建 Subject(提供身份信息);


# 8.Subject #
是 Shiro 的核心对象,基本所有身份验证、授权都是通过 Subject 完成。

##1、身份信息获取##

##2、身份验证 ##

void login(AuthenticationToken token) throws AuthenticationException;
boolean isAuthenticated();
boolean isRemembered();

通过 login 登录,如果登录失败将抛出相应的 AuthenticationException,如果登录成功调用 isAuthenticated 就会返回 true,即已经通过身份验证;

如果 isRemembered 返回 true,表示是通过记住我功能登录的而不是调用 login 方法登录的。isAuthenticated/isRemembered 是互斥的,即如果其中一个返回 true,另一个返回 false。

3、角色授权验证

boolean hasRole(String roleIdentifier);

void checkRole(String roleIdentifier) throws 

hasRole 进行角色验证,验证后返回 true/false;而 checkRole 验证失败时抛出 AuthorizationException 异常。

4、权限授权验证

boolean isPermitted(String permission);
...
isPermitted 进行权限验证,验证后返回 true/false;而 checkPermission 验证失败时抛出 AuthorizationException。

5、会话

Session getSession(); //相当于getSession(true)
Session getSession(boolean create);  
类似于 Web 中的会话。如果登录成功就相当于建立了会话,接着可以使用 getSession 获取;如果 create=false 如果没有会话将返回 null,而 create=true 如果没有会话会强制创建一个。

6、退出

void logout();

7、RunAs

void runAs(PrincipalCollection principals) throws NullPointerException, IllegalStateException;
boolean isRunAs();
PrincipalCollection getPreviousPrincipals();
PrincipalCollection releaseRunAs();

RunAs 即实现 “允许 A 假设为 B 身份进行访问”;通过调用 subject.runAs(b) 进行访问;

接着调用 subject.getPrincipals 将获取到 B 的身份;此时调用 isRunAs 将返回 true;而 a 的身份需要通过 subject. getPreviousPrincipals 获取;如果不需要 RunAs 了调用 subject. releaseRunAs 即可。

8、多线程

V execute(Callable callable) throws ExecutionException;

void execute(Runnable runnable);

Callable associateWith(Callable callable);

Runnable associateWith(Runnable runnable);

实现线程之间的 Subject 传播,因为 Subject 是线程绑定的;因此在多线程执行中需要传播到相应的线程才能获取到相应的 Subject。最简单的办法就是通过 execute(runnable/callable 实例) 直接调用;或者通过 associateWith(runnable/callable 实例) 得到一个包装后的实例;它们都是通过:1、把当前线程的 Subject 绑定过去;2、在线程执行结束后自动释放。

Subject 自己不会实现相应的身份验证 / 授权逻辑,而是通过 DelegatingSubject 委托给 SecurityManager 实现;及可以理解为 Subject 是一个面门。

对于 Subject 的构建一般没必要我们去创建;一般通过 SecurityUtils.getSubject() 获取:

public static Subject getSubject() {
    Subject subject = ThreadContext.getSubject();
    if (subject == null) {
        subject = (new Subject.Builder()).buildSubject();
        ThreadContext.bind(subject);
    }
    return subject;

即首先查看当前线程是否绑定了 Subject,如果没有通过 Subject.Builder 构建一个然后绑定到现场返回。

如果想自定义创建,可以通过:

new Subject.Builder().principals(身份).authenticated(true/false).buildSubject()

这种可以创建相应的 Subject 实例了,然后自己绑定到线程即可。在 new Builder() 时如果没有传入 SecurityManager,自动调用 SecurityUtils.getSecurityManager 获取;也可以自己传入一个实例。

对于 Subject 我们一般这么使用:

1.login 2.授权(hasRole/...) 5.logout
至于多线程身份传播, ..


#9.web集成 #
 ini配置中的 url配置

    [urls]
    /login=anon
    /unauthorized=anon
    /static/**=anon
    /authenticated=authc
    /role=authc,roles[admin]
    /permission=authc,perms["user:create"]

url模式匹配顺序,url模式匹配顺序按照配置中声明的顺序匹配,即从头开始使用第一个匹配的url模式对于的拦截器.如:

    /bb/**=filter1
    /bb/aa=filter2
    /**=filter3
    
如果请求是/bb/aa, 将会按照顺序匹配, 

默认登录URL配置:[访问这些地址首先判断用户有没有登录, 如果没有登录默认会跳转到登录页面..,默认是/login.jsp,]

authc.loginUrl=/login

 

你可能感兴趣的:(shiro笔记)