shiro 学习(二)


title: shiro学习(二)
date: 2020-11-14
tags:

  • spring
  • springboot
  • shiro
    categories:
  • spring
  • springboot
  • shiro学习(二)

一、使用MD5和Salt(Shiro编码加密)

1.1 MD5算法

  • **特点:**MD5算法不可逆如何内容相同无论执行多少次md5生成结果始终是一致。
  • **作用:**一般用来加密或者签名(校验和:一般用来验证文件是否完整和正确)。
  • **生成结果:**始终是一个16进制32位长度字符串。

1.2 加密流程

实际应用是将盐和散列后的值存在数据库中,自动 realm从数据库取出盐和加密后的值由 shiro完成密码校验。

1.3 加密测试

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

        //使用MD5 + Salt
        Md5Hash md5Hash1 = new Md5Hash("123","O#2$1q");
        System.out.println(md5Hash1.toHex());

        //使用MD5 + Salt + hash散列
        Md5Hash md5Hash2 = new Md5Hash("123","O#2$1q",024);
        System.out.println(md5Hash2.toHex());
    }
}

加密后的结果

1.4 自定义realm中测试加密

1.4.1 编写自定义realm
public class CustomerMD5Realm extends AuthorizingRealm {
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //在token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();

        //匹配用户名,后面用户名和密码应该在数据库中查询
        if ("zhy".equals(principal)){
            //参数:1.用户名 2.密码 3.注册时的Salt 4.Realm名字。通过返回这些参数Shiro会自动进行密码校验
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    principal,
                    "fc753b6f31731d70c1e9cf8befa23197",
                    ByteSource.Util.bytes("O#2$1q"),
                    this.getName());
            return simpleAuthenticationInfo;
        }

        return null;
    }
}
1.4.2 测试自定义Realm
public class TestCustomerMd5RealmAuthenticator{
    public static void main(String[] args) {
        //1.创建安全管理器对象、
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //2.给自定义realm添加算法类型(MD5),和hash散列次数
        CustomerMD5Realm realm = new CustomerMD5Realm();
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //2.1 设置md5算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //2.2 设置散列次数
        credentialsMatcher.setHashIterations(1024);
        //2.3 给自定义realm设置散列凭证匹配器
        realm.setCredentialsMatcher(credentialsMatcher);

        //3给安全管理器设置自定义Realm
        securityManager.setRealm(realm);

        //4.给SecurityUtils是指安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        //5.通过SecurityUtils获取Subject
        Subject subject = SecurityUtils.getSubject();

        //6.创建token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("zhy","123");

        try {
            //7.用户验证
            subject.login(token);
            System.out.println(subject.isAuthenticated());
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("username error!");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("password error!");
        }
    }
}

校验通过 MD5+Salt+hash 散列加密的密码时需要告诉Shiro这是加密过后的密码。

二、Shiro 授权

2.1 定义

授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

  • 主体:主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
  • 资源:在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
  • 权限:安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面
    查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等。如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)。
  • 角色:角色代表了操作集合,可以理解为权限的集合一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

2.2 三种授权方式

编程式:通过写 if/else 授权代码块完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
    //有权限
} else {
    //无权限
}
注解式:通过在执行的 Java 方法上放置相应的注解完成:
@RequiresRoles("admin")
public void hello() {
    //有权限
}
//没有权限将抛出相应的异常
JSP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>

2.3 字符串通配符权限

规则:资源标识符:操作:对象实例 ID” 即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串,“:”表示资源/操作/实例的分割;“,”表示操作的分割;“*”表示任意资源/操作/实例。

  • 单个资源单个权限:

    用户拥有资源“user”的“update”权限:subject().checkPermissions("system:user:update");

  • 单个资源多个权限:

    用户拥有资源“user”的“update”和“delete”权限:subject().checkPermissions("user:update", "user:delete");subject().checkPermissions("system:user:update,delete");

  • 单个资源全部权限:

    subject().checkPermissions("user:*");(推荐)

    subject().checkPermissions("user");

  • 单个实例单个权限:

    对资源 user 的 1 实例拥有update 权限:subject().checkPermissions("user:update:1");

  • Shiro 对权限字符串缺失部分的处理

    如“user:view”等价于“user:view:*”;而“organization”等价于“organization:*”或者“organization:*:*”。可以这么理解,这种方式实现了前缀匹配。

    另外如“user:*”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、“user:*:1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”等。即*可以匹配所有,不加*可以进行前缀匹配;但是如“*:view”不能匹配“system:user:view”,需要使用“*:*:view”,即后缀匹配必须指定前缀(多个冒号就需要多个*来匹配)。

2.4 测试

Realm编写
public class CustomerMD5Realm extends AuthorizingRealm {
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        System.out.println("身份信息(用户名):"+primaryPrincipal);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //将根据用户名从数据库中查询的角色信息赋值给权限对象
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");
        //将根据用户名从数据库中查询的权限信息赋值给权限对象
        simpleAuthorizationInfo.addStringPermission("user:*:1");
        simpleAuthorizationInfo.addStringPermission("product:create");

        return simpleAuthorizationInfo;
    }

    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //在token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();

        //匹配用户名,后面用户名和密码应该在数据库中查询
        if ("zhy".equals(principal)){
            //参数:1.用户名 2.密码 3.注册时的Salt 4.Realm名字。通过返回这些参数Shiro会自动进行密码校验
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    principal,
                    "aef9adbba635d84b7d7760e40293202e",
                    ByteSource.Util.bytes("O#2$1q"),
                    this.getName());
            return simpleAuthenticationInfo;
        }

        return null;
    }
}
授权测试
public class TestCustomerMd5RealmAuthenticator{
    public static void main(String[] args) {
        //1.创建安全管理器对象、
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //2.给自定义realm添加算法类型(MD5),和hash散列次数
        CustomerMD5Realm realm = new CustomerMD5Realm();
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //2.1 设置md5算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //2.2 设置散列次数
        credentialsMatcher.setHashIterations(1024);
        //2.3 给自定义realm设置散列凭证匹配器
        realm.setCredentialsMatcher(credentialsMatcher);

        //3给安全管理器设置自定义Realm
        securityManager.setRealm(realm);

        //4.给SecurityUtils是指安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        //5.通过SecurityUtils获取Subject
        Subject subject = SecurityUtils.getSubject();

        //6.创建token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("zhy","123");

        try {
            //7.用户验证
            subject.login(token);
            System.out.println(subject.isAuthenticated());
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("username error!");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("password error!");
        }

        //授权
        if (subject.isAuthenticated()){
            //基于角色权限控制
            System.out.println(subject.hasRole("super"));
            //基于多角色权限控制(与关系)
            System.out.println(subject.hasAllRoles(Arrays.asList("admin","super")));
            //是否具有其中一个角色(或关系)
            boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
            for (boolean aBoolean : booleans) {
                System.out.println(aBoolean);
            }
            System.out.println("======================");
            //字符串通配符权限:资源标识符:操作:对象实例 ID
            System.out.println("权限:"+subject.isPermitted("user:create:1"));
            System.out.println("权限:"+subject.isPermitted("product:create:1"));
            //同时具有哪些权限
            System.out.println(subject.isPermittedAll("product:create:1", "user:delete:1"));
            //分别具有哪些权限
            boolean[] permitted = subject.isPermitted("user:create:1", "order:create");
            for (boolean b : permitted) {
                System.out.println(b);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值