Shiro的简单实现

权限的管理

权限管理包括用户的身份认证授权两部分,简称认证授权。
身份认证:打游戏登录
授权:1级的鬼剑士不能去18级的地图打怪

Shiro简介

Shiro是阿帕奇下的一个开源,它把系统安全相关功能抽取出来,实现用户身份认证、权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

Shiro的核心架构

核心架构图
核心架构图subject:主体,外部应用通过subject接口进行认证授权
Security Manager:安全管理部分
Realm:域,数据来源

Authenticator:认证器
Authorizer:授权器
Session Manager:会话管理,web服务的时候session会交给它管理
Session Dao:回话的DAO层
Cache Manager:缓存管理,用户权限数据会在缓存,提高性能
Realm:域,数据来源
Cryptography:算法部分

Shiro的认证

关键对象

Subject:主体,访问系统的用户。
Principal:身份信息,主体进行认证的标识,必须具有唯一性,一个主体可以有多个身份,但必须有一个主身份。相当于账号
Credential:凭证信息,只有主体自己才知道的安全信息,相当于密码

认证流程

认证流程

先跑起来(认证)

快速建一个shiro demo
maven项目

		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.0</version>
        </dependency>

在resources新建ini文件

[users]
zhangsan=123
lisi=123456

模拟登陆

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;

public class TestAuthenticator {
    public static void main(String[] args) {
        //1·创建安全管理器对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //2·给安全管理器设置realm
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        //3·SecurityManager 全局安全工具类
        SecurityUtils.setSecurityManager(securityManager);

        //4·关键对象 subject主体
        Subject subject = SecurityUtils.getSubject();

        //5·创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","1232");
        try {
            subject.login(token);

            System.out.println("认证成功" +  subject.isAuthenticated() + token);
        }catch (UnknownAccountException e){
            System.out.println("用户名不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
    }
}

简单的实现就完成了,为了替换掉ini作为数据来源,就得自定义Realm。

自定义Realm


/**
 * 自定义Realm,将认证/授权数据的来源转为数据库的实现
 */
public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //在token中获取用户名字
        String principal = (String) token.getPrincipal();

        //根据身份信息使用mybatis查询相关数据库

        //假如数据库里有这个用户
        if ("zhaoshanhe".equals(principal)){
            //参数1:返回数据库中正确的用户名  //参数2:返回数据库中的密码  //参数3:提供当前Realm的名字 this.getName
            return new SimpleAuthenticationInfo(principal,"123456",getName());
        }
        return null;
    }
}

/**
 * 使用自定义Realm
 */
public class TestCustomerRealmAuthenticator {
    public static void main(String[] args) {
        //创建securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //设置自定义realm
        defaultSecurityManager.setRealm(new CustomerRealm());
        //设置安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("zhaoshanhe", "123456");

        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e){
            System.out.println("用户名不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }

    }
}

上边代码通过脑补jdbc实现了用户登录,但密码是明文

明文密码?MD5?加盐?

加密效果

public static void main(String[] args) {
        //使用MD5
        Md5Hash hash = new Md5Hash("123456");
        System.out.println(hash.toHex());

        //使用MD5 + salt(盐)处理,盐完后要替换成随机字符串
        Md5Hash hash1 = new Md5Hash("123456","x0*366go");
        System.out.println(hash1.toHex());

        //使用MD5 + 盐 + hash散列
        Md5Hash hash2 = new Md5Hash("123456","x0*366go",1024);
        System.out.println(hash2.toHex());
    }
e10adc3949ba59abbe56e057f20f883e
ecb5c4a53e65c8db187106601468d0ca
cac6f8e93a1d711cb13b6415e5b835f1

下面开始测试三种加密程度,自定义realm里脑补数据库查密码

MD5不加盐登录

public class TestCustomerMd5RealmAuthenticator {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //注入realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        realm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(realm);

        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //安全工具获取subject
        Subject subject = SecurityUtils.getSubject();
        //认证
        UsernamePasswordToken token = new UsernamePasswordToken("zhaoshanhe","123456");
        try {
            subject.login(token);
            System.out.println("登录成功");
        }catch (UnknownAccountException e){
            System.out.println("用户名不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
    }
}

md5不加盐

import java.lang.reflect.ParameterizedType;

public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取身份信息
        String principal = (String) token.getPrincipal();

        //根据用户名查数据库
        if ("zhaoshanhe".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"e10adc3949ba59abbe56e057f20f883e",getName());
        }
        return null;
    }
}

登录成功

md5加盐操作
登录代码不变,改掉自定义realm里假密码ecb5c4a53e65c8db187106601468d0ca和盐

//ByteSource.Util.bytes里边放的是盐
  @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取身份信息
        String principal = (String) token.getPrincipal();

        //根据用户名查数据库
        if ("zhaoshanhe".equals(principal)){
            return new SimpleAuthenticationInfo(principal,
                    "ecb5c4a53e65c8db187106601468d0ca",
                    ByteSource.Util.bytes("x0*366go"),
                    getName());
        }
        return null;
    }
登录成功

MD5加盐+ 散列
改掉自定义realm里数据库假密码cac6f8e93a1d711cb13b6415e5b835f1

 public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        CustomerMd5Realm realm = new CustomerMd5Realm();

        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //使用的算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //告诉匹配器散列多少次
        hashedCredentialsMatcher.setHashIterations(1024);

        realm.setCredentialsMatcher(hashedCredentialsMatcher);

        //注入realm
        defaultSecurityManager.setRealm(realm);

        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //安全工具获取subject
        Subject subject = SecurityUtils.getSubject();
        //认证
        UsernamePasswordToken token = new UsernamePasswordToken("zhaoshanhe","123456");
        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e){
            System.out.println("用户名不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
    }
登录成功

Shiro的授权

授权:就是访问控制,控制谁能访问那些资源,认证通过后需要分配权限才能访问系统资源,某些资源没有权限是不能访问的。就是之前说的登上账号后,1级不能去10级的地图。

关键对象

谁对什么做什么。who,what,how
who:主体(Subject)
what:资源(Resource)
hwo:权限/许可(Premission)

授权流程

授权流程
通过认证进入系统后检查操作权限

授权方式

授权方式有两种:
基于角色的访问控制和基于资源的访问控制

权限字符串

权限字符串的规则是:资源标识符:操作:资源实例标识符
比如:
用户创建权限:user:create或者user:create:*
用户修改实例shop的权限:user:update:shop
用户实例shop的所有权限:user:*:shop

基于角色的授权

授权是基于认证的,所以使用上边的MD5加随机盐加散列作为认证

		
		 //认证用户进行授权
        //isAuthenticated是判断有没有认证通过
        if (subject.isAuthenticated()){

            //基于角色权限控制,hasRole,返回是boolean,是说subject有没有这个角色
            System.out.println(subject.hasRole("admin"));
        }
			//基于多角色权限控制,这个用户必须同时拥有这两个角色才true
            System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));

            //是否有其中一个角色
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
            for (boolean aBoolean : booleans){
                System.out.println(aBoolean);
            }

它走了自定义realm的doGetAuthorizationInfo方法,所以要覆盖

@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        System.out.println("身份信息:" + primaryPrincipal);

        //上边拿到了身份信息,下来就根据身份信息去数据库拿角色信息,以及权限信息
        //脑补数据局查到了这个用户有admin和user角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //把数据库查到的角色信息赋值刚给权限对象
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");

        return simpleAuthorizationInfo;
    }

基于权限字符串的授权

if (subject.isAuthenticated()){
	//基于权限字符串的访问控制 资源标识符:操作:资源类型,
    System.out.println(subject.isPermitted("user:update:shop"));
    
    //同时具有哪些权限
     System.out.println(subject.isPermittedAll("user:create:shop", "product:*"));

     //分别有哪些权限
     boolean[] permitted = subject.isPermitted("user:*:shop", "order:*:*");
     for (boolean b : permitted) {
         System.out.println(b);
     }
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        System.out.println("身份信息:" + primaryPrincipal);

        //上边拿到了身份信息,下来就根据身份信息去数据库拿角色信息,以及权限信息
        //脑补数据局查到了这个用户有admin和user角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //把数据库查到的角色信息赋值刚给权限对象
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");

        //将数据库中查到的权限信息赋值给权限对象
        simpleAuthorizationInfo.addStringPermission("user:*:shop");
        return simpleAuthorizationInfo;
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值