文章目录
摘自
shiro官网:
https://www.infoq.cn/article/apache-shiro?itm_source=infoq_en&itm_medium=link_on_en_item&itm_campaign=item_in_other_langs
1、什么是shiro
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
2、 shiro解决问题
认证 - 用户身份识别,常被称为用户“登录”;
授权 - 访问控制;
密码加密 - 保护或隐藏数据防止被偷窥;
会话管理 - 每用户相关的时间敏感的状态。
3、shiro优点
易于使用
广泛性
安全性
灵活性
web能力
可插拔
支持
4、核心概念
既然已经描述了 Shiro 的好处,那就让我们看看它的 API,好让你能够有个感性认识。Shiro 架构有三个主要概念 - Subject,SecurityManager 和 Realms。
Subject
表示当前用户操作,获取当前用户
术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
...
Subject currentUser = SecurityUtils.getSubject();
SecurityManager
Subject 的“幕后”推手是 SecurityManager。Subject 代表了当前用户的安全操作,SecurityManager 则管理所有用户的安全操作。
普通的 Java 代码、Spring XML、YAML、.properties 和.ini 文件等。基本来讲,能够实例化类和调用 JavaBean 兼容方法的任何配置形式都可使用。
为此,Shiro 借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI 易于阅读、使用简单并且需要极少依赖。
用INI配置Shiro
[main]
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
# Base64 encoding (less text):
cm.storedCredentialsHexEncoded = false
iniRealm.credentialsMatcher = $cm
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
Realms
从这个意义上讲,Realm 实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。
使用方式(粗略)
认证
认证是核实用户身份的过程。也就是说,当用户使用应用进行认证时,他们就在证明他们就是自己所说的那个人。有时这也理解为“登录”。
subject登录
//1. 接受提交的当事人和证书:
AuthenticationToken token =
new UsernamePasswordToken(username, password);
//2. 获取当前 Subject:
Subject currentUser = SecurityUtils.getSubject();
//3. 登录:
currentUser.login(token);
控制登录失败
//3. 登录:
try {
currentUser.login(token);
} catch (IncorrectCredentialsException ice) {
…
} catch (LockedAccountException lae) {
…
}
…
catch (AuthenticationException ae) {…
}
授权
授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web 页面等等。多数用户执行访问控制是通过使用诸如角色和权限这类概念完成的。
角色检查
if ( subject.hasRole(“administrator”) ) {
// 显示‘Create User’按钮
} else {
// 按钮置灰?
}
权限检查
if ( subject.isPermitted(“user:create”) ) {
// 显示‘Create User’按钮
} else {
// 按钮置灰?
}
密码加密
加密
String encodedPassword = new Sha512Hash(password, salt, count).toBase64();
密码
AesCipherService cipherService = new AesCipherService();
cipherService.setKeySize(256);
// 创建一个测试密钥:
byte[] testKey = cipherService.generateNewKey();
// 加密文件的字节:
byte[] encrypted = cipherService.encrypt(fileBytes, testKey);
会话管理
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
Session session = subject.getSession();
session.getAttribute("key", someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);
shiro学习何使用
登录
参照
https://jinnianshilongnian.iteye.com/blog/2019547
1、ini文件配置角色(略、看连接)
2、ini配置读取,通过java代码实现(略、看连接)
3、读取jdbc文件实现
jar包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</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>
<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>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
shiro-jdbc-realm.ini
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=UTF-8
dataSource.username=root
dataSource.password=123456
dataSource.validationQuery=select 1
dataSource.testOnBorrow=true
dataSource.testWhileIdle=true
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm
test.java
@Test
public void testJDBCRealm() {
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<org.apache.shiro.mgt.SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");
//2、得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhou", "123");
try {
//4、登录,即身份验证
subject.login(token);
System.out.println("登陆成功");
} catch (AuthenticationException e) {
//5、身份验证失败
e.printStackTrace();
}
Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
//6、退出
subject.logout();
}
授权
users表
user_roles表
roles_permissions表
sql配置
delete from users;
delete from user_roles;
delete from roles_permissions;
insert into users(username, password, password_salt) values('zhang', '123', null);
insert into user_roles(username, role_name) values('zhang', 'role1');
insert into user_roles(username, role_name) values('zhang', 'role2');
insert into roles_permissions(role_name, permission) values('role1', '+user1+10');
insert into roles_permissions(role_name, permission) values('role1', 'user1:*');
insert into roles_permissions(role_name, permission) values('role1', '+user2+10');
insert into roles_permissions(role_name, permission) values('role1', 'user2:*');
shiro-jdbc-authorizer.ini
[main]
#自定义authorizer
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
#自定义permissionResolver
#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
permissionResolver=org.ssmdemo.shiro2.permission.BitAndWildPermissionResolver
authorizer.permissionResolver=$permissionResolver
#自定义rolePermissionResolver
rolePermissionResolver=org.ssmdemo.shiro2.permission.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver
securityManager.authorizer=$authorizer
#自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=UTF-8
dataSource.username=root
dataSource.password=123456
dataSource.validationQuery=select 1
dataSource.testOnBorrow=true
dataSource.testWhileIdle=true
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true
securityManager.realms=$jdbcRealm
MyRolePermissionResolver.java
package org.ssmdemo.shiro2.permission;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
import java.util.Arrays;
import java.util.Collection;
/**
* <p>User: Zhang Kaitao
* <p>Date: 14-1-26
* <p>Version: 1.0
*/
public class MyRolePermissionResolver implements RolePermissionResolver {
@Override
public Collection<Permission> resolvePermissionsInRole(String roleString) {
if("role1".equals(roleString)) {
return Arrays.asList((Permission)new WildcardPermission("menu:*"));
}
return null;
}
}
BitAndWildPermissionResolver.java
package org.ssmdemo.shiro2.permission;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
public class BitAndWildPermissionResolver implements PermissionResolver {
@Override
public Permission resolvePermission(String permissionString) {
if(permissionString.startsWith("+")) {
return new BitPermission(permissionString);
}
return new WildcardPermission(permissionString);
}
}
BitPermission.java
package org.ssmdemo.shiro2.permission;
import com.alibaba.druid.util.StringUtils;
import junit.framework.Assert;
import org.apache.shiro.authz.Permission;
/**
* 规则
* +资源字符串+权限位+实例ID
*
* 以+开头 中间通过+分割
*
* 权限:
* 0 表示所有权限
* 1 新增 0001
* 2 修改 0010
* 4 删除 0100
* 8 查看 1000
*
* 如 +user+10 表示对资源user拥有修改/查看权限
*
* 不考虑一些异常情况
*
* <p>User: Zhang Kaitao
* <p>Date: 14-1-26
* <p>Version: 1.0
*/
public class BitPermission implements Permission {
private String resourceIdentify;
private int permissionBit;
private String instanceId;
public BitPermission(String permissionString) {
String[] array = permissionString.split("\\+");
if(array.length > 1) {
resourceIdentify = array[1];
}
if(StringUtils.isEmpty(resourceIdentify)) {
resourceIdentify = "*";
}
if(array.length > 2) {
permissionBit = Integer.valueOf(array[2]);
}
if(array.length > 3) {
instanceId = array[3];
}
if(StringUtils.isEmpty(instanceId)) {
instanceId = "*";
}
}
@Override
public boolean implies(Permission p) {
if(!(p instanceof BitPermission)) {
return false;
}
BitPermission other = (BitPermission) p;
if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) {
return false;
}
if(!(this.permissionBit ==0 || (this.permissionBit & other.permissionBit) != 0)) {
return false;
}
if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) {
return false;
}
return true;
}
@Override
public String toString() {
return "BitPermission{" +
"resourceIdentify='" + resourceIdentify + '\'' +
", permissionBit=" + permissionBit +
", instanceId='" + instanceId + '\'' +
'}';
}
}
MyRolePermissionResolver.java
package org.ssmdemo.shiro2.permission;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
import java.util.Arrays;
import java.util.Collection;
/**
* <p>User: Zhang Kaitao
* <p>Date: 14-1-26
* <p>Version: 1.0
*/
public class MyRolePermissionResolver implements RolePermissionResolver {
@Override
public Collection<Permission> resolvePermissionsInRole(String roleString) {
if("role1".equals(roleString)) {
return Arrays.asList((Permission)new WildcardPermission("menu:*"));
}
return null;
}
}
AuthorizerTest.java
package org.ssmdemo.shiro2;
import junit.framework.Assert;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.junit.Test;
/**
* <p>User: Zhang Kaitao
* <p>Date: 14-1-26
* <p>Version: 1.0
*/
public class AuthorizerTest extends BaseTest {
@Test
public void testIsPermitted() {
login("classpath:shiro-authorizer.ini", "zhang", "123");
//判断拥有权限:user:create
Assert.assertTrue(subject().isPermitted("user1:update"));
Assert.assertTrue(subject().isPermitted("user2:update"));
//通过二进制位的方式表示权限
Assert.assertTrue(subject().isPermitted("+user1+2"));//新增权限
Assert.assertTrue(subject().isPermitted("+user1+8"));//查看权限
Assert.assertTrue(subject().isPermitted("+user2+10"));//新增及查看
Assert.assertFalse(subject().isPermitted("+user1+4"));//没有删除权限
Assert.assertTrue(subject().isPermitted("menu:view"));//通过MyRolePermissionResolver解析得到的权限
}
@Test
public void testIsPermitted2() {
login("classpath:shiro-jdbc-authorizer.ini", "zhang", "123");
//判断拥有权限:user:create
Assert.assertTrue(subject().isPermitted("user1:update"));
Assert.assertTrue(subject().isPermitted("user2:update"));
//通过二进制位的方式表示权限
Assert.assertTrue(subject().isPermitted("+user1+2"));//新增权限
Assert.assertTrue(subject().isPermitted("+user1+8"));//查看权限
Assert.assertTrue(subject().isPermitted("+user2+10"));//新增及查看
Assert.assertFalse(subject().isPermitted("+user1+4"));//没有删除权限
Assert.assertTrue(subject().isPermitted("menu:view"));//通过MyRolePermissionResolver解析得到的权限
}
}
加密/编码
散列加密 + 访问次数限制
shiro-jdbc-hashedCredentialsMatcher.ini
[main]
#credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher=org.ssmdemo.shiro3.credentials.RetryLimitHashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
credentialsMatcher.storedCredentialsHexEncoded=true
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=123456
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
jdbcRealm.permissionsLookupEnabled=true
jdbcRealm.saltStyle=COLUMN
jdbcRealm.authenticationQuery=select password, concat(username,password_salt) from users where username = ?
jdbcRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$jdbcRealm
RetryLimitHashedCredentialsMatcher.java
package org.ssmdemo.shiro3.credentials;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import javax.security.auth.login.AccountLockedException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>User: Zhang Kaitao
* <p>Date: 14-1-28
* <p>Version: 1.0
*/
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
private Ehcache passwordRetryCache;
public RetryLimitHashedCredentialsMatcher() {
CacheManager cacheManager = CacheManager.newInstance(CacheManager.class.getClassLoader().getResource("ehcache.xml"));
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String)token.getPrincipal();
//retry count + 1
Element element = passwordRetryCache.get(username);
if(element == null) {
element = new Element(username , new AtomicInteger(0));
passwordRetryCache.put(element);
}
AtomicInteger retryCount = (AtomicInteger)element.getObjectValue();
if(retryCount.incrementAndGet() > 5) {
//if retry count > 5 throw
throw new ExcessiveAttemptsException();
}
boolean matches = super.doCredentialsMatch(token, info);
if(matches) {
//clear retry count
passwordRetryCache.remove(username);
}
return matches;
}
}
package org.ssmdemo.shiro3;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.converters.AbstractConverter;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.junit.Test;
public class PasswordTest extends BaseTest{
@Test
public void testRetryLimitHashedCredentialsMatcherWithMyRealm() {
//login("classpath:shiro-retryLimitHashedCredentialsMatcher.ini", "liu", "123");
for(int i = 1; i <= 5; i++) {
try {
login("classpath:shiro-jdbc-hashedCredentialsMatcher.ini", "liu", "234");
} catch (Exception e) {/*ignore*/System.out.println("123");}
}
login("classpath:shiro-jdbc-hashedCredentialsMatcher.ini", "liu", "123");
System.out.println(4);
}
}
ssm+shiro使用
推荐文章连接:
https://blog.csdn.net/qq_29410905/article/details/80364305