10-shiro

作者: 16325 | 来源:发表于2020-07-15 17:17 被阅读0次

简介

这里新开一篇文章,记录下shiro的相关知识。
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
首先,我们从外部来看Shiro吧,即从应用程序角度的来观察如何使用Shiro完成工作。如下图:


image.png
  • Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

  • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

  • Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

  • 从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。

身份验证

身份验证,即在应用中谁能证明他就是他本人。一般提供如他们的身份ID一些标识信息来表明他就是他本人,如提供身份证,用户名/密码来证明。

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

  • principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。

  • credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
    最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。

  • Realm Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

  • 多Realm配置。 会按照realm声明的顺序进行使用

  • 自定义的Realm一般继承AuthorizingRealm即可。

授权

Shiro支持三种方式的权限控制

  • 编程式:通过写if/else授权代码块完成:
    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) { ... }

  • 注解式:通过在执行的Java方法上放置相应的注解完成: 没有权限将抛出相应的异常;
    @RequiresRoles("admin")
    public void hello() {
    //有权限
    }

  • JSP/GSP标签:在JSP/GSP页面通过相应的标签完成:
    <shiro:hasRole name="admin">
    <!— 有权限 —>
    </shiro:hasRole>

角色/资源
Shiro提供的checkRole/checkRoles和hasRole/hasAllRoles,不同的地方是它在判断为假的情况下会抛出UnauthorizedException异常
Shiro提供了isPermitted和isPermittedAll用于判断用户是否拥有某个权限或所有权限

与Web集成

Shiro提供了与Web集成的支持,其通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后判断URL是否需要登录/权限等工作。

  • mvc的话就配置filter
  • springboot的话就ShiroFilterFactoryBean

会话管理

Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管JavaSE还是JavaEE环境都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web的透明支持、SSO单点登录的支持等特性。即直接使用Shiro的会话管理可以直接替换如Web容器的会话管理。

会话管理器管理着应用中所有Subject的会话的创建、维护、删除、失效、验证等工作。是Shiro的核心组件,顶层组件SecurityManager直接继承了SessionManager,且提供了SessionsSecurityManager实现直接把会话管理委托给相应的SessionManager,DefaultSecurityManager及DefaultWebSecurityManager默认SecurityManager都继承了SessionsSecurityManager

Shiro提供了三个默认实现:

  • DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;

  • ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;并且可以设置session相关cookies的name等信息。

  • DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。

  • 可以正常配置会话监听器

  • 会话存储/持久化 Shiro提供SessionDAO用于会话的CRUD,即DAO(Data Access Object)模式实现:

  • 会话验证

单点登录

Shiro 1.2开始提供了Jasig CAS单点登录的支持,单点登录主要用于多系统集成。
大体流程如下:
1、访问客户端需要登录的页面http://localhost:9080/ client/,此时会跳到单点登录服务器https://localhost:8443/ server/login?service=https://localhost:9443/ client/cas;
2、如果此时单点登录服务器也没有登录的话,会显示登录表单页面,输入用户名/密码进行登录;
3、登录成功后服务器端会回调客户端传入的地址:https://localhost:9443/client/cas?ticket=ST-1-eh2cIo92F9syvoMs5DOg-cas01.example.org,且带着一个ticket;
4、客户端会把ticket提交给服务器来验证ticket是否有效;如果有效服务器端将返回用户身份;
5、客户端可以再根据这个用户身份获取如当前系统用户/角色/权限信息。

接入流程:

  • 导入shiro-cas 的依赖
  • 自定义CasRealm
public class MyCasRealm extends CasRealm {  
    private UserService userService;  
    public void setUserService(UserService userService) {  
        this.userService = userService;  
    }  
    @Override  
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
        String username = (String)principals.getPrimaryPrincipal();  
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
        authorizationInfo.setRoles(userService.findRoles(username));  
        authorizationInfo.setStringPermissions(userService.findPermissions(username));  
        return authorizationInfo;  
    }  
}   
  • 配置
<bean id="casRealm" class="com.github.zhangkaitao.shiro.chapter13.realm.MyCasRealm">  
    <property name="userService" ref="userService"/>  
    ……  
    <property name="casServerUrlPrefix" value="https://localhost:8443/chapter14-server"/>  
    <property name="casService" value="https://localhost:9443/chapter14-client/cas"/>  
</bean>  

casServerUrlPrefix:是CAS Server服务器端地址;
casService:是当前应用CAS服务URL,即用于接收并处理登录成功后的Ticket的;

  • cas的token验证
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
    <property name="failureUrl" value="/casFailure.jsp"/>  
</bean>  
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    <property name="securityManager" ref="securityManager"/>  
    <property name="loginUrl" value="https://localhost:8443/chapter14-server/login?service=https://localhost:9443/chapter14-client/cas"/>  
    <property name="successUrl" value="/"/>  
    <property name="filters">  
        <util:map>  
            <entry key="cas" value-ref="casFilter"/>  
        </util:map>  
    </property>  
    <property name="filterChainDefinitions">  
        <value>  
            /casFailure.jsp = anon  
            /cas = cas  
            /logout = logout  
            /** = user  
        </value>  
    </property>  
</bean>   

loginUrl:https://localhost:8443/chapter15-server/login表示服务端端登录地址,登录成功后跳转到?service参数对于的地址进行客户端验证及登录;

“/cas=cas”:即/cas地址是服务器端回调地址,使用CasFilter获取Ticket进行登录。

OAuth2集成

OAuth角色

  • 资源拥有者(resource owner):能授权访问受保护资源的一个实体,可以是一个人,那我们称之为最终用户;如新浪微博用户zhangsan;

  • 资源服务器(resource server):存储受保护资源,客户端通过access token请求资源,资源服务器响应受保护资源给客户端;存储着用户zhangsan的微博等信息。

  • 授权服务器(authorization server):成功验证资源拥有者并获取授权之后,授权服务器颁发授权令牌(Access Token)给客户端。

  • 客户端(client):如新浪微博客户端weico、微格等第三方应用,也可以是它自己的官方应用;其本身不存储资源,而是资源拥有者授权通过后,使用它的授权(授权令牌)访问受保护资源,然后客户端把相应的数据展示出来/提交到服务器。“客户端”术语不代表任何特定实现(如应用运行在一台服务器、桌面、手机或其他设备)。

认证服务流程

资源服务流程

  • 1、首先通过如http://localhost:8080/chapter17-server/userInfo? access_token=828beda907066d058584f37bcfd597b6进行访问;
  • 2、该控制器会验证access token的有效性;如果无效了将返回相应的错误,客户端再重新进行授权;
  • 3、如果有效,则返回当前登录用户的用户名。

客户端

  • OAuth2AuthenticationFilter。 该filter的作用类似于FormAuthenticationFilter用于oauth2客户端的身份验证控制;如果当前用户还没有身份验证,首先会判断url中是否有code(服务端返回的auth code),如果没有则重定向到服务端进行登录并授权,然后返回auth code;接着OAuth2AuthenticationFilter会用auth code创建OAuth2Token,然后提交给Subject.login进行登录;接着OAuth2Realm会根据OAuth2Token进行相应的登录逻辑。
  • 1、首先判断有没有服务端返回的error参数,如果有则直接重定向到失败页面;
  • 2、接着如果用户还没有身份验证,判断是否有auth code参数(即是不是服务端授权之后返回的),如果没有则重定向到服务端进行授权;
  • 3、否则调用executeLogin进行登录,通过auth code创建OAuth2Token提交给Subject进行登录;
  • 4、登录成功将回调onLoginSuccess方法重定向到成功页面;
  • 5、登录失败则回调onLoginFailure重定向到失败页面。

OAuth2Realm

  • 此Realm首先只支持OAuth2Token类型的Token;然后通过传入的auth code去换取access token;再根据access token去获取用户信息(用户名),然后根据此信息创建AuthenticationInfo;如果需要AuthorizationInfo信息,可以根据此处获取的用户名再根据自己的业务规则去获取。

总结

  • 认证服务提供登录功能,返回authCode
  • 客户端的Realm通过authCode去认证服务器获取accessToken
  • 用accessToken访问资源服务器中的资源,获取用户信息(这步是不是可以换成从token中提取用户名,这样就不必要调用资源服务接口了)
  • 拿到用户信息后客户端shiro做登录操作,本地处理角色等

JWT

下面介绍一下无状态的,基于jwt的认证处理方式。
比如使用了oauth2,得到了一个accessToken,就当作是jwt,里边包含了用户名。然后没有资源服务提供什么用户信息接口了。

有状态方式

  • 有个jwt,每次请求验证这个jwt(可能有缓存,每次不需要真正执行查询等操作)
  • 验证之后,存到session中一些东西,以后会用

无状态方式

  • 有个jwt,每次请求验证jwt,从中获取用户名,角色等,这些都在jwt中本身包含(也可能有缓存和上边是一样的)
  • 每次验证,可以刷新token有效期
  • 可以使用redis等存储jwt用来验证

相关文章

  • 10-shiro

    简介 这里新开一篇文章,记录下shiro的相关知识。Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提...

网友评论

      本文标题:10-shiro

      本文链接:https://www.haomeiwen.com/subject/vdidhktx.html