shiro学习笔记


摘自
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值