RBAC

RBCA

RBCA zh_CN

Shiro

Apache Shiro 是一个强大且易于使用的 Java 安全框架,负责执行身份验证、授权、加密和会话管理。

通过 Shiro 的易于理解的 API,您可以快速而轻松地保护任何应用程序,从最小的移动应用到最大的 Web 和企业应用。

Shiro 提供了应用程序安全 API,用于执行以下方面:

  • 身份验证 - 验证用户身份,通常称为用户“登录”
  • 授权 - 访问控制
  • 加密 - 保护或隐藏数据免受窥探
  • 会话管理 - 每个用户的时限敏感状态

Subject->SecurityManager:
SecurityManager->Realms:

shiro

shiro zh_CN

Hello world

  • pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
  • shiro.ini

create this file under the classpath.

[users]
ryo=123
wang=123
  • ShiroTest.java
@Test
public void testHelloworld() {
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini"); org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("ryo", "123"); try {
subject.login(token);
} catch (AuthenticationException e) {
//login falied
} assertEquals(true, subject.isAuthenticated()); //assert user has logined. //logout
subject.logout();
}

Realms

Realms(领域)充当 Shiro 与您的应用程序安全数据之间的“桥梁”或“连接器”。

当需要实际与安全相关的数据进行交互,例如从用户帐户执行身份验证(登录)和授权(访问控制)时,Shiro 会查找配置为应用程序的一个或多个领域中的许多信息。

  • Realm.java
public interface Realm {
String getName(); //返回一个唯一的Realm名字 boolean supports(AuthenticationToken var1); //判断此Realm是否支持此Token AuthenticationInfo getAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException; //根据Token获取认证信息
}
  • MyRealm.java
public class MyRealm implements Realm {
@Override
public String getName() {
return "firstRealm";
} @Override
public boolean supports(AuthenticationToken authenticationToken) {
//仅支持UsernamePasswordToken类型的Token
return authenticationToken instanceof UsernamePasswordToken;
} @Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal(); //get username
String password = new String((char[]) authenticationToken.getCredentials()); //get password
if (!"ryo".equals(username)) {
throw new UnknownAccountException();
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException();
} //如果身份认证验证成功,返回一个AuthenticationInfo实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
  • shiro-realm.ini

create this file under the classpath.

#declear realm
firstRealm=com.ryo.shiro.MyRealm
#point the realms impls of securityManager
securityManager.realms=$firstRealm
  • test()
@Test
public void testRealm() {
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro-realm.ini");
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("ryo", "123"); try {
subject.login(token);
} catch (AuthenticationException e) {
} assertEquals(true, subject.isAuthenticated()); subject.logout();
}

multi-realm

  • define another realm SecondRealm.java
public class SecondRealm implements Realm {
public String getName() {
return "secondRealm";
} public boolean supports(AuthenticationToken authenticationToken) {
return authenticationToken instanceof UsernamePasswordToken;
} public AuthenticationInfo getAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
String password = new String((char[]) authenticationToken.getCredentials());
if (!"wang".equals(username)) {
throw new UnknownAccountException();
}
if (!"123".equals(password)) {
throw new IncorrectCredentialsException();
}
return new SimpleAuthenticationInfo(username, password, getName());
}
}
  • define shiro-multi-realm.ini
[main]
#define
firstRealm=com.ryo.shiro.FirstRealm
secondRealm=com.ryo.shiro.SecondRealm
#use
securityManager.realms=$firstRealm,$secondRealm
  • test()
@Test
public void testMultiRealm() {
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro-multi-realm.ini"); org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wang", "123"); try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
} Assert.assertEquals(true, subject.isAuthenticated()); subject.logout();
}

Notice

The realm worked only after you used it.

JDBC Realm

  • Add jars info your pom.xml, here I user MySQL and druid datasource for test.
<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>
  • Here are some sql to init database.
DROP DATABASE IF EXISTS shiro;
CREATE DATABASE shiro;
USE shiro; CREATE TABLE users (
id BIGINT AUTO_INCREMENT,
username VARCHAR(100),
password VARCHAR(100),
password_salt VARCHAR(100),
CONSTRAINT pk_users PRIMARY KEY (id)
)
CHARSET = utf8
ENGINE = InnoDB;
CREATE UNIQUE INDEX idx_users_username ON users (username); CREATE TABLE user_roles (
id BIGINT AUTO_INCREMENT,
username VARCHAR(100),
role_name VARCHAR(100),
CONSTRAINT pk_user_roles PRIMARY KEY (id)
)
CHARSET = utf8
ENGINE = InnoDB;
CREATE UNIQUE INDEX idx_user_roles ON user_roles (username, role_name); CREATE TABLE roles_permissions (
id BIGINT AUTO_INCREMENT,
role_name VARCHAR(100),
permission VARCHAR(100),
CONSTRAINT pk_roles_permissions PRIMARY KEY (id)
)
CHARSET = utf8
ENGINE = InnoDB;
CREATE UNIQUE INDEX idx_roles_permissions ON roles_permissions (role_name, permission); INSERT INTO users (username, password) VALUES ('wang', '123');
INSERT INTO users (username, password) VALUES ('ryo', '123');
  • shiro-jdbc-realm.ini
[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3307/shiro
dataSource.username=root
dataSource.password=${youOwnSQLPassword}
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm ;1、varName=className auto create an instance of class.
;2、varName.property=val auto call the set()
;3、$varname reference an object define before;
  • test()
@Test
public void testJDBCRealm() {
Factory<org.apache.shiro.mgt.SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini"); org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("ryo", "123"); try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
} Assert.assertEquals(true, subject.isAuthenticated()); subject.logout();
}

Authenticator

  • 在单领域应用程序中,ModularRealmAuthenticator 将直接调用单个领域。

  • 如果您希望使用自定义的 Authenticator 实现配置 SecurityManager,您可以在 shiro.ini 中进行配置,例如:

[main]
authenticator = com.foo.bar.CustomAuthenticator securityManager.authenticator = $authenticator

SecurityManager.java

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
}

Authenticator.java

public interface Authenticator {
AuthenticationInfo authenticate(AuthenticationToken var1) throws AuthenticationException;
}

AuthenticationStrategy

当为应用程序配置了两个或更多领域时,ModularRealmAuthenticator 依赖于内部的 AuthenticationStrategy 组件来确定身份验证尝试成功或失败的条件。

AuthenticationStrategy 类 描述
AtLeastOneSuccessfulStrategy 如果一个或多个领域成功验证,则将考虑整体尝试成功。如果没有一个验证成功,则尝试失败。
FirstSuccessfulStrategy 仅使用从第一个成功验证的领域返回的信息。所有后续领域将被忽略。如果没有一个验证成功,则尝试失败。
AllSuccessfulStrategy 所有配置的领域必须成功验证才能考虑整体尝试成功。如果有任何一个验证不成功,则尝试失败。

1、这里定义了三个用于测试的领域:

  • FirstRealm ryo/123 成功,返回 ryo/123

  • SecondRealm wang/123 成功,返回 wang/123

  • ThirdRealm ryo/123 成功,返回 ryo@gmail.com/123

2、shiro-authenticator-all-success.ini

ModularRealmAuthenticator 默认使用 AtLeastOneSuccessfulStrategy 实现,因为这是最常用的策略。

但是,如果需要,您可以配置不同的策略。

[main]
#point out securityManager's authenticator
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator #Point out securityManager.authenticator's authenticationStrategy
allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy #Define and use realms
firstRealm=com.ryo.shiro.FirstRealm
secondRealm=com.ryo.shiro.SecondRealm
thirdRealm=com.ryo.shiro.ThirdRealm
securityManager.realms=$firstRealm,$thirdRealm

3、AuthenticatorTest.java

@Test
public void testAllSuccessfulStrategyWithSuccess() {
Subject subject = getSubjectByPath("classpath:shiro-authenticator-all-success.ini"); UsernamePasswordToken token = new UsernamePasswordToken("ryo", "123");
subject.login(token); PrincipalCollection principalCollection = subject.getPrincipals();
assertEquals("ryo,ryo@gmail.com", principalCollection.toString());
} private Subject getSubjectByPath(String configFilePath) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory(configFilePath); SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
return SecurityUtils.getSubject();
}

<label class="label label-info">提示</label>

如果您想自己创建自己的 AuthenticationStrategy 实现,您可以使用 org.apache.shiro.authc.pam.AbstractAuthenticationStrategy 作为起点。

  • OnlyOneAuthenticatorStrategy.java
public class OnlyOneAuthenticatorStrategy extends AbstractAuthenticationStrategy {
//Simply returns the aggregate argument without modification.
@Override
public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
return super.beforeAllAttempts(realms, token);
} //Simply returns the aggregate method argument, without modification.
@Override
public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
return super.beforeAttempt(realm, token, aggregate);
} /**
* Base implementation that will aggregate the specified singleRealmInfo into the aggregateInfo and then returns the aggregate.
* @param realm the realm that was just consulted for AuthenticationInfo for the given token.
* @param token the AuthenticationToken submitted for the subject attempting system log-in.
* @param singleRealmInfo the info returned from a single realm. 单个realm信息
* @param aggregateInfo the aggregate info representing all realms in a multi-realm environment. 总信息
* @param t the Throwable thrown by the Realm during the attempt, or null if the method returned normally.
* @return
* @throws AuthenticationException
*/
@Override
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
AuthenticationInfo info; if(singleRealmInfo == null) {
info = aggregateInfo;
} else if(aggregateInfo == null) {
info = singleRealmInfo;
} else {
info = merge(singleRealmInfo, aggregateInfo); if(info.getPrincipals().getRealmNames().size() > 1) {
throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " +
"could not be authenticated by any configured realms. Please ensure that only one realm can " +
"authenticate these tokens.");
}
} return info;
} //Merges the specified info argument into the aggregate argument and then returns an aggregate for continued use throughout the login process.
@Override
protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {
return super.merge(info, aggregate);
} //Base implementation that will aggregate the specified singleRealmInfo into the aggregateInfo and then returns the aggregate.
@Override
public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
return super.afterAllAttempts(token, aggregate);
}
}
  • shiro-authenticator-onlyone-success.ini
[main]
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator onlyOneAuthenticatorStrategy=com.ryo.shiro.authenticator.strategy.OnlyOneAuthenticatorStrategy
securityManager.authenticator.authenticationStrategy=$onlyOneAuthenticatorStrategy #define and uer realms.
firstRealm=com.ryo.shiro.FirstRealm
secondRealm=com.ryo.shiro.SecondRealm
securityManager.realms=$firstRealm,$secondRealm
  • test()
@Test
public void testOnlyOneAuthenticatorStrategy() {
Subject subject = getSubjectByPath("classpath:shiro-authenticator-onlyone-success.ini"); UsernamePasswordToken token = new UsernamePasswordToken("ryo", "123");
subject.login(token); PrincipalCollection principalCollection = subject.getPrincipals();
assertEquals("ryo", principalCollection.toString());
}
  • if you change the shiro-authenticator-onlyone-success.ini into
#define and uer realms.
firstRealm=com.ryo.shiro.FirstRealm
thirdRealm=com.ryo.shiro.ThirdRealm
securityManager.realms=$firstRealm,$thirdRealm

You will get an error as following.

org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken]
could not be authenticated by any configured realms. Please ensure that only one realm can authenticate these tokens.

本文由博客一文多发平台 OpenWrite 发布!

Shiro-00-shiro 概览的更多相关文章

  1. SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能

    SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...

  2. Shiro (Shiro + JWT + SpringBoot应用)

    Shiro (Shiro + JWT + SpringBoot应用) 目录 Shiro (Shiro + JWT + SpringBoot应用) 1.Shiro的简介 2.Shiro + JWT + ...

  3. 【shiro】shiro学习笔记1 - 初识shiro

    [TOC] 认证流程 st=>start: Start e=>end: End op1=>operation: 构造SecurityManager环境 op2=>operati ...

  4. 菜鸟手把手学Shiro之shiro认证流程

    一.使用的spring boot +mybatis-plus+shiro+maven来搭建项目框架 <!--shiro--> <dependency> <groupId& ...

  5. shiro整合shiro多验证登录(账号密码登录和使用手机验证码登录)

    1. 首先新建一个shiroConfig  shiro的配置类,代码如下: @Configuration是标识这个类是一个配置文件,在启动时会加载这个类里面的内容,这个配置文件的位置的一定一定一定不能 ...

  6. shiro框架学习-2-springboot整合shiro及Shiro认证授权流程

    1. 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  7. 菜鸟手把手学Shiro之shiro授权流程

    一.首先我们从整体去看一下授权流程,然后再根据源码去分析授权流程.如下图: 流程如下: 1.首先调用 Subject.isPermitted*/hasRole*接口,其会委托给 SecurityMan ...

  8. Maven+Spring+Hibernate+Shiro+Mysql简单的demo框架(一)

    相关的maven的 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...

  9. 十、 Spring Boot Shiro 权限管理

    使用Shiro之前用在spring MVC中,是通过XML文件进行配置. 将Shiro应用到Spring Boot中,本地已经完成了SpringBoot使用Shiro的实例,将配置方法共享一下. 先简 ...

  10. Spring Boot Shiro 权限管理 【转】

    http://blog.csdn.net/catoop/article/details/50520958 主要用于备忘 本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro ...

随机推荐

  1. 03-ASIC和FPGA中的时钟结构

    1 ASIC 中时钟的结构 ASIC电路中的时钟的结构.这是一个非常典型的MCU的时钟结构图.它的时钟结构和功能的划分.首先,我们通过外部振荡器发送了一个8MHz的时钟给PLL,经过分分频和倍频产生更 ...

  2. MCU芯片设计流程

    MCU设计流程 1.产品开发整体流程 Integrated Product Development(IPD) TR-Technique Review-技术评审 xDCP-管理层决定是否开发 这里的验证 ...

  3. Pickle反序列化学习

    什么是Pickle? 很简单,就是一个python的序列化模块,方便对象的传输与存储.但是pickle的灵活度很高,可以通过对opcode的编写来实现代码执行的效果,由此引发一系列的安全问题 Pick ...

  4. [转帖](1.3)sql server for linux 配置mssql-conf(即SSCM)

    https://blog.51cto.com/ultrasql/2152021 目录 [配置mssql-conf] 启用SQL Server代理 修改SQL Server排序规则 配置客户反馈 修改默 ...

  5. [转帖]金仓数据库KingbaseES V8R6 中unlogged表

    KingbaseESV8R6有一种表称为unlogged,在该表新建的索引也属于unlogged.和普通表的区别是,对该表进行DML操作时候不将该表的变更记录变更写入到wal文件中.在数据库异常关机或 ...

  6. 解决Word等打开嵌入的文件提示 包含有害内容 无法打开的问题

    最近打开文件时提示: 从网上找了一下 最简单的解决办法是: 新建一个文件, 输入如下内容 导入注册表 每次打开时不进行 文件有效性的检查即可. 为了省事 我多加了几个版本的 如果是excel  将 w ...

  7. Grafana监控Redis的使用情况

    Grafana监控Redis的使用情况 前言 最近在进行性能测试, 为了比较直观的将监控数据展示出来. 自己在周末时学习了下prometheus, 与之前的一个node_exporter一样, 本次进 ...

  8. 【JS 逆向百例】反混淆入门,某鹏教育 JS 混淆还原

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  9. 数字预失真(DPD)小试

    前言 射频功放的增益响应并非线性的,受到放大管饱和效应的影响,功放不可避免地出现非线性.甚至具有记忆效应的失真.这种非线性失真不仅产生高阶谐波,还会产生互调干扰,降低带内信噪比,影响带外信号.因此,需 ...

  10. Git的使用(二):远程仓库

    在github上创建远程仓库 本地创建Git仓库适合自己一个人完成工程,但是实际情况中我们需要其他人来协作开发,此时就可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份.常用的远程仓库就是g ...