写点什么

深入浅出 Shiro 系列

发布于: 2020 年 06 月 29 日
深入浅出Shiro系列

写在前面:

小伙伴儿们,大家好!这次让我们一起来学习 Shiro 权限框架吧!

思维导图:

1,初识 Shiro

1.1,简介;

  • Apache Shiro 是一个强大且易用的 Java 安全框架,执行身份验证、授权、密码和会话管理。使用 Shiro 的易于理解的 API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

1.2,入门配置;

创建 Maven 工程命名为 Shiro,结构图如下;

直接引入依赖:

 <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->    <dependency>      <groupId>org.apache.shiro</groupId>      <artifactId>shiro-core</artifactId>      <version>1.5.3</version>    </dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.0-alpha1</version> </dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
复制代码


log4j.properties 配置文件:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache librarieslog4j.logger.org.apache=WARN
# Springlog4j.logger.org.springframework=WARN
# Default Shiro logginglog4j.logger.org.apache.shiro=TRACE
# Disable verbose logginglog4j.logger.org.apache.shiro.util.ThreadContext=WARNlog4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
复制代码


shiro.ini 文件(放在 resources 目录下),以键值对地形式存放:

[users]java=123456jack=123
复制代码


1.3,程序文件;

我们新建一个 HelloWorld 类:

package com.java;
import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;
public class HelloWorld {
public static void main(String[] args) { // 读取配置文件,初始化SecurityManager工厂 Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini"); // 获取securityManager实例 SecurityManager securityManager=factory.getInstance(); // 把securityManager实例绑定到SecurityUtils SecurityUtils.setSecurityManager(securityManager); // 得到当前执行的用户 Subject currentUser=SecurityUtils.getSubject(); // 创建token令牌,用户名/密码 UsernamePasswordToken token=new UsernamePasswordToken("java", "123456"); try{ // 身份认证 currentUser.login(token); System.out.println("身份认证成功!"); }catch(AuthenticationException e){ e.printStackTrace(); System.out.println("身份认证失败!"); } // 退出 currentUser.logout(); }}
复制代码


  • 首先通过 new IniSecurityManagerFa ctory 并指定一个 ini 配置文件来创建一个 SecurityManager 工厂;

  • 接着获取 SecurityManager 并绑定到 SecurityUtils,这是一个全局设置,设置一次即可;

  • 通过 SecurityUtils 得到 Subject,其会自动绑定到当前线程;如果在 web 环境在请求结束时需要解除绑定;然后获取身份验证的 Token,如用户名 / 密码;

  • 调用 subject.login 方法进行登录,其会自动委托给 SecurityManager.login 方法进行登录;

  • 如果身份验证失败请捕获 Authenticat ionException 或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountExceptio n(错误的帐号)、ExcessiveAttempts Exception(登录失败次数过多)、In correctCredentialsException (错误的凭证)、ExpiredCredentialsException (过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;

  • 最后可以调用 subject.logout 退出,其会自动委托给 SecurityManager.logout 方法退出。

运行:

从如上代码可总结出身份验证的步骤

  1. 收集用户身份 / 凭证,即如用户名 / 密码;

  2. 调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功;

  3. 最后调用 Subject.logout 进行退出操作。

2,Shiro 身份认证;

2.1,Subject 认证主体;

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

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

credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。另外两个相关的概念是之前提到的 SubjectRealm,分别是主体及验证主体的数据源。

2.2,身份认证流程;

流程如下:

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

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

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

  4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthen ticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;

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

2.3,Realm;

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

realm 种类很多,例如常见的 jdbc realm,jndi realm,text realm,我们这里就了解一下 jdbc realm,其他的不做叙述;

首先先建立数据库 db_shiro,然后添加表 users(表名称不可改),添加一条数据;

配置文件 jdbc_realm.ini,这与上文的 shiro.ini 是一样的;

[main]jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealmdataSource=com.alibaba.druid.pool.DruidDataSourcedataSource.driverClassName=com.mysql.jdbc.DriverdataSource.url=jdbc:mysql://localhost:3306/db_shirodataSource.username=rootdataSource.password=123456jdbcRealm.dataSource=$dataSourcesecurityManager.realms=$jdbcRealm
复制代码


导入依赖

<!-- mysql 数据库及 druid 连接池-->    <dependency>      <groupId>mysql</groupId>      <artifactId>mysql-connector-java</artifactId>      <version>5.1.25</version>    </dependency>    <dependency>      <groupId>com.alibaba</groupId>      <artifactId>druid</artifactId>      <version>0.2.23</version>    </dependency>
复制代码


测试程序(把路径更改一下即可)

package com.java;
import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;
public class JdbcRealmTest {
public static void main(String[] args) { // 读取配置文件,初始化SecurityManager工厂 Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:jdbc_realm.ini"); // 获取securityManager实例 SecurityManager securityManager=factory.getInstance(); // 把securityManager实例绑定到SecurityUtils SecurityUtils.setSecurityManager(securityManager); // 得到当前执行的用户 Subject currentUser=SecurityUtils.getSubject(); // 创建token令牌,用户名/密码 UsernamePasswordToken token=new UsernamePasswordToken("java", "123"); try{ // 身份认证 currentUser.login(token); System.out.println("身份认证成功!"); }catch(AuthenticationException e){ e.printStackTrace(); System.out.println("身份认证失败!"); } // 退出 currentUser.logout(); }}
复制代码


运行结果



好了,今天就先分享到这里了,下期继续给大家带来 Shiro 相关方面的学习!更多干货、优质文章,欢迎关注我的原创技术公众号~


发布于: 2020 年 06 月 29 日阅读数: 2049
用户头像

程序员的路,会越来越精彩! 2020.04.30 加入

公众号:程序员的时光 记录学习编程的一路时光,从小白到现在也能稳操胜券; 主要从事Java后台开发,数据结构与算法,设计模式等等; 欢迎一起交流,分享经验,学习进步!

评论

发布
暂无评论
深入浅出Shiro系列