Shiro

Shiro 是什么?

Apache Shiro 是 Java  的一个安全(权限)框架。

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

Shiro 功能简介

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

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能进行哪些操作。

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

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

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

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

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

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

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

Shiro 架构

Subject:应用代码直接交互的对象是 Subject,通过Subject进行一些操作,Subject 代表了当前“用户,与 Subject  的所有交互都会委托给 SecurityManager, Subject 其实是一个门面,SecurityManager 才是实际的执行者。

SecurityManager安全管理器。所有与安全有关的操作都会与 SecurityManager 交互,其管理着所有 Subject,它是 Shiro 的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中 DispatcherServlet 的角色。

Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限), SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法。也需要从 Realm 得到用户相应的角色/ 权限进行验证用户是否能进行某些操作。

Authenticator:负责 Subject 认证,是一个扩展点,可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过。

Authorizer:授权器、即访问控制器,用来决定用户是否有权限进行相应的操作,控制着用户能访问应用或网页中的哪些功能。

SessionManager:管理 Session 生命周期的组件,而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境。

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

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

Shiro 与 Spring集成

在Spring的 applicationContext.xml 中对 Shiro 的主要架构进行配置。

集成步骤

1. 配置 SecurityManager 安全管理器

    
        
        
        
        
        
            
                
            
        
        
        
    

 2. 配置 CacheManager 缓存控制器

需要加入ehcache 的 jar 包及配置文件

     
        
    

3. 配置 authenticator 认证

    
        
        
            
        
    

4. 配置 Reaml

    
        
            
                
                
                
                
            
        
    

5. 配置 LifecycleBeanPostProcessor

可以自动的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法

6. 启用 IOC 容器中使用 shiro 的注解

必须在配置了 LifecycleBeanPostProcessor 之后才可以使用.



    

7. 配置 ShiroFilter


        
        
        
        
        
        
        
        
    
    
    
    
    
    
    

//页面权限类
public class FilterChainDefinitionMapBuilder {
    public LinkedHashMap buildFilterChainDefinitionMap(){
        LinkedHashMap map = new LinkedHashMap<>();
        map.put("/login.jsp","anon");
        map.put("/shiro/login","anon");
        map.put("/shiro/logout","logout");
        map.put("/admin.jsp","authc,roles[admin]");
        map.put("/user.jsp","authc,roles[user]");
        map.put("/list.jsp","user");
        map.put("/**","authc");
        return map;
    }
}

Web.xml 中对 ShiroFilter 进行配置

DelegatingFilterProxy 是Filter的一个代理对象

    
        shiroFilter
        org.springframework.web.filter.DelegatingFilterProxy
        
            targetFilterLifecycle
            true
        
    
    
        shiroFilter
        
        /*
    

ShiroFilter 工作原理

前端发送请求经过 ShiroFilter 过滤器,读取filterChainDefinitions配置,根据请求的url 和权限匹配对应的过滤器链,然后按顺序执行下去,如果没有权限重定向回安全页面。

必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 一致.,若不一致, 则会抛出: NoSuchBeanDefinitionException。因为 Shiro 会来 IOC 容器中查找和 名字对应的 filter bean。

Shiro 默认过滤器

Shiro_第1张图片

URL 权限采取第一次匹配优先的方式,即从头开始使用第一个匹配的 url 模式对应的拦截器链。

认证

身份验证:一般需要提供如身份 ID 等一些标识信息来表明登录者的身份,如提供 email,用户名/密码来证明。

在 shiro 中,用户需要提供 principals (身份) credentials(证明)给 shiro,从而验证用户身份。

principals:身份,即用户的标识属性,可以是任何属性,如ID、用户名、 邮箱等,唯一即可

credentials:证明/凭证,即只有用户知道的安全值,如密码/数字证书等。

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

自定义Handler类

添加 @Controller 注解表示为SpringMVC的控制器,进行认证初步判断,若已认证则进行操作,未认证转去Realm 进行认证。

@RequestMapping("/shiro")
@Controller
public class ShiroHandler {
    @RequestMapping("/login")
    public String login(@RequestParam("username")String username,@RequestParam("password") String password){
        //1.获取当前的 Subject. 调用 SecurityUtils.getSubject();
        Subject currentUser = SecurityUtils.getSubject();
        //2.测试当前的用户是否已经被认证. 即是否已经登录,调动 Subject 的 isAuthenticated()。
        if (!currentUser.isAuthenticated()) {
            //3.没有认证,把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // rememberme
            token.setRememberMe(true);
            try {
                //4.执行登录.
                currentUser.login(token);
                //5.登录的逻辑,由Realm 自定义的方法进行密码校验
            }
            // 所有认证时异常的父类.
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
                System.out.println("登陆失败:"+ae.getMessage());
            }
        }
        return "redirect:/list.jsp";
    }
}

创建自定义的 Realm 类

继承 org.apache.shiro.realm.AuthorizingRealm类,实现 doGetAuthenticationInfo() 方法

1、首先调用 Subject.login(token) 进行登录,其会自动委托给 SecurityManager。

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

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

4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证。

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

Reaml 的认证逻辑:

首先将重写的 doGetAuthenticationInfo 参数的 AuthenticationToken token 转换为 UsernamePasswordToken。

然后我们可以通过用户名或者其他信息查询数据库保存的用户信息,进行判断是否登录成功。

MD5 盐值加密

1. 为什么要使用 MD5 加密?

出于安全性的考虑,我们保存在数据库的密码或者其他重要信息不能以明文形式进行保存,所以需要采用加密等方式进行存储,并且加密方式是不可逆,MD5可以满足我们的要求,但是只进行加密还不够安全,因为可能两个人密码一样,在数据库保存的密文也就相同,所以我们需要使用MD5的盐值进行进一步加密,salt(盐值)采用用户的唯一字段,这样就可以保证密文不同,达到安全性。

2. 加密步骤:

1)在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 构造器。

2)使用 ByteSource salt = ByteSource.Util.bytes() 来计算盐值。参数使用唯一字段或随机字符串。

3)延值需要唯一, 一般使用随机字符串或 user id 等唯一字段。

4)使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations),来计算盐值加密后的密码的值.

AuthenticationStrategy

验证策略:在applicationContext 的 SecurityManager 中进行验证策略的指定。

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

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

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

ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略。

授权

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

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

资源(Resource):在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法等等都是资源。用户只要授权后才能访问。

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

角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。

Shiro 支持三种方式的授权

编程式:通过写if/else 授权代码块完成。

注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常。

JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成。

权限注解:

@RequiresAuthentication:表示当前Subject已经通过login 进行了身份验证即, Subject. isAuthenticated() 返回 true。

@RequiresUser:表示当前 Subject 已经身份验证或者通过记 住我登录的。 • @RequiresGuest:表示当前Subject没有身份验证或通过记住 我登录过,即是游客身份。

@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND):表示当前 Subject 需要角色 admin 和user

@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR):表示当前 Subject 需要权限 user:a 或 user:b。

JSP/GSP 标签:

guest 标签:用户没有身份验证时显示相应信息,即游客访问信息。

Shiro_第2张图片

user 标签:用户已经经过认证/记住我登录后显示相应的信息。

pincipal 标签:显示用户身份信息,默认调用 Subject.getPrincipal() 获取,即 Primary Principal。

authenticated 标签:用户已经身份验证通过,即 Subject.login登录成功,不是通过记住我登录的。

 notAuthenticated 标签:用户未进行身份验证,记住我也是未进行身份验证。

hasRole 标签:如果当前 Subject 有角色将显示 body 体内容。

hasAnyRoles 标签:如果当前Subject有任意一个 角色(或的关系)将显示body体内容。

 lacksRole:如果当前 Subject 没有角色将显 示 body 体内容。

拦截器

Shiro_第3张图片

Shiro_第4张图片

授权流程

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

授权需要继承 AuthorizingRealm 类, 并实现其 doGetAuthorizationInfo 方法
AuthorizingRealm 类继承自 AuthenticatingRealm, 但没有实现 AuthenticatingRealm 中的
doGetAuthenticationInfo, 所以认证和授权只需要继承 AuthorizingRealm 就可以了. 同时实现他的两个抽象方法。

认证和记住我

subject.isAuthenticated()表示用户进行了身份验证登录的, 即使通过 Subject.login 进行了登录。

subject.isRemembered():表示用户是通过记住我登录的。

访问一般网页:如个人在主页之类的,我们使用user 拦截器即可,user 拦截器只要用户登录 (isRemembered() || isAuthenticated()) 通过即可访问成功。

访问特殊网页:如我的订单,提交订单页面,我们使用 authc 拦截器,authc 拦截器会判断用户是否是通过 Subject.login(isAuthenticated()==true)登录的,如 果是才放行,否则会跳转到登录页面叫你重新登录。

 

你可能感兴趣的:(知识点复习)