ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

116.Shiro(一):Shiro基本概念,Shiro认证、授权的基本使用

2021-05-30 16:01:28  阅读:209  来源: 互联网

标签:println 116 shiro org apache import 基本概念 Shiro subject


目录

一、权限的基本概念

1.什么是权限管理

2.什么是身份认证

3.什么是授权

二、Shiro的基本了解

1.什么是Shiro

2.shiro的核心架构

三、Shrio第一个程序用户认证

1.基本概念

(1)Subject:主体

(2)Principal:身份信息

(3)credential:凭证信息

2.认证流程

3.实例编写

(1)创建项目

(2)引入shiro依赖

(3)引入shiro配置文件

(4)编写控制类

(5)测试

4.认证源码的解读

5.实例优化:自定义Realm

四、DM5和Salt

1.MD5算法

(1)特点

(2)作用

(3)生成结果

2.Salt(盐)

3.md5+salt+hash散列实例

五、Shiro中认证:MD5+Salt+Hash的实现

1.查询密码对应的密文

2.Shiro中MD5+Salt+Hash的实现

六、Shiro中的授权

1.基本概念

2.授权方式

3.权限字符串

4.授权的编程实现

5.授权的实例代码

七、打赏请求


一、权限的基本概念

1.什么是权限管理

权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则控制用户可以且仅能访问自己被授权的资源。

权限管理包括身份认证和授权两部分,简称认证授权。

 

2.什么是身份认证

判断一个用户是否为合法用户的处理过程。

最常见的方式是用户名和口令,还可以是指纹,刷脸等方式。

 

3.什么是授权

授权,即访问控制,控制谁能访问哪些资源。

 

二、Shiro的基本了解

1.什么是Shiro

官网地址:http://shiro.apache.org/

 

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

 

2.shiro的核心架构

 

三、Shrio第一个程序用户认证

1.基本概念

(1)Subject:主体

访问系统的用户,进行认证的都是主体

(2)Principal:身份信息

是主体进行身份认证的标识,具有唯一性。一个主体可以有多个身份,但是必须有一个主身份

(3)credential:凭证信息

只有主体自己知道的安全信息

 

2.认证流程

主体通过身份信息,凭证信息生成另外,然后校验令牌是否合法。

 

3.实例编写

(1)创建项目

创建好后自动引入maven,然后新增两个resources文件夹:

(2)引入shiro依赖

(3)引入shiro配置文件

注意,shiro是以ini为结尾的配置文件,而不是常规的properties或者yml。

配置文件只需要放在resources下面的任意一个目录中即可。

 

注意:真正项目中,我们是用不到ini这个文件的,这个文件用于我们学习中书写系统相关权限。省去了编辑数据库相关的操作,简化学习过程

 

上面的[users]是固定写死的:

(4)编写控制类

package com.xupeng;

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(realm提供数据相关)
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

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

        //4.关键对象:subject对象
        Subject subject = SecurityUtils.getSubject();

        //5.创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xupeng","456");
        try{
            System.out.println("认证状态:"+subject.isAuthenticated());
            subject.login(token);//用户登录验证
            System.out.println("认证状态:"+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败,认证用户不存在");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败,密码错误");
        }


    }
}

(5)测试

我们会发现状态变了,证明登录成功

 

4.认证源码的解读

(1)最终用户名的认证是在SimpleAccountRealm中:

(2)密码的校验是在AuthenticatingRealm类中:

一旦我们密码不正确,他会报IncorrectCredentialsException异常,源码看这里也就清晰了

 

5.实例优化:自定义Realm

上述我们例子中,用户名密码是写在ini文件中,但是在实际使用中肯定是放在数据库,这边我们来模拟数据库先做一个简单的例子

 

CustomerRealm:

package com.xupeng.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

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

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //在token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();
        System.out.println(principal);
        //根据身份信息使用jdbc mybatis查询相关数据库
        if("xupeng".equals(principal)){
            //参数1:数据库中正确用户名
            //参数2:数据库中正确密码
            //参数3:提供当前Realm名字
            SimpleAuthenticationInfo simpleAuthenticationInfo  = new SimpleAuthenticationInfo("xupeng","456",this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

TestCustomerRealmAuthenticator:

package com.xupeng;

import com.xupeng.realm.CustomerRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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.subject.Subject;

/**
 * 使用自定义的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("xupeng","456");

        try {

            subject.login(token);
            System.out.println(subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败,认证用户不存在");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败,密码错误");
        }
    }
}

测试:

 

四、DM5和Salt

数据库不能明文存储密码,我们需要MD5加密,并且加Salt(盐)。

 

1.MD5算法

(1)特点

  • MD5算法不可逆。
  • 如果内容相同,无论执行多少次md5生成的结果都是一样的

(2)作用

一般用于加密或者签名。

签名这里我们来解释一下,我们下载软件的时候经常官网会看到一个md5后缀的文件,这个文件用于校验你下载的软件是否存在缺失。我们将下载的文件整体经过md5加密后,应该跟官网提供的md5后缀的那个文件相同才对。

(3)生成结果

md5生成的结果始终是一个16进制32位长度的字符串

 

2.Salt(盐)

(1)MD5算法的问题
现在网上有穷举网站,如果我们直接密码存md5,拿着MD5去穷举网站比对,还是比较容易比对出的。所以我们给密码再加一点自己的东西,这个东西就叫盐。增加比对的复杂性,保证密码的相对安全。

 

3.md5+salt+hash散列实例

 

五、Shiro中认证:MD5+Salt+Hash的实现

1.查询密码对应的密文

TestShiroMD5
package com.xupeng;

import org.apache.shiro.crypto.hash.Md5Hash;

public class TestShiroMD5 {
    public static void main(String[] args) {
        //直接使用md5算法
        Md5Hash md5Hash = new Md5Hash("456");
        System.out.println(md5Hash.toHex());

        //使用md5+salt
        Md5Hash md5Hash1 = new Md5Hash("456","xp");
        System.out.println(md5Hash1.toHex());

        //使用md5+salt+hash散列
        //其中,1024表示对加盐结果进行1024次散列
        Md5Hash md5Hash2 = new Md5Hash("456","xp",1024);
        System.out.println(md5Hash2.toHex());
    }
}

2.Shiro中MD5+Salt+Hash的实现

CustomerMd5Realm:

package com.xupeng.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * 自定义Realm实现,加入md5+salt+hash散列
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //在token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();
        System.out.println(principal);
        //根据身份信息使用jdbc mybatis查询相关数据库
        if("xupeng".equals(principal)){
            //参数1:数据库中正确用户名
            //参数2:数据库中正确MD5密码
            //参数3:提供当前Realm名字
//            SimpleAuthenticationInfo simpleAuthenticationInfo  = new SimpleAuthenticationInfo(
//                    "xupeng",
//                    "250cf8b51c773f3f8dc8b4be867a9a02",
//                    this.getName());

            //参数1:数据库中正确用户名
            //参数2:数据库中正确MD5密码+salt后的密码。如果加了散列次数,就设置散列后的密码
            //参数3:注册时的随机盐
            //参数4:提供当前Realm名字
            SimpleAuthenticationInfo simpleAuthenticationInfo  = new SimpleAuthenticationInfo(
                    "xupeng",
                    "4ca532fb479910d125d72992a3f57b33",
                    ByteSource.Util.bytes("xp"),
                    this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

TestCustomerMd5RealmAuthenticator:

package com.xupeng;

import com.xupeng.realm.CustomerMd5Realm;
import com.xupeng.realm.CustomerRealm;
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.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

/**
 * 使用自定义的Realm,加入md5+salt+hash散列
 */
public class TestCustomerMd5RealmAuthenticator {
    public static void main(String[] args) {
        //创建SecurityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //注入Realm
        CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();
        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        credentialsMatcher.setHashIterations(1024);
        customerMd5Realm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerMd5Realm);
        //设置安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类来获取subject
        Subject subject = SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xupeng","456");

        try {

            subject.login(token);
            System.out.println(subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败,认证用户不存在");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败,密码错误");
        }
    }
}

 

六、Shiro中的授权

授权,即访问控制,控制谁能访问哪些资源。

1.基本概念

(1)主体:Subject

(2)资源:Resource

(3)权限/许可:Permission

 

2.授权方式

  • 基于角色的访问控制
  • 基于资源的访问控制

 

3.权限字符串

对于基于资源的访问控制,权限字符串的规则是:

资源标识符:操作:资源实例标识符

意思是对那个资源的哪个实例有什么操作

 

实例:

用户创建权限:

user:create, 或者user:create:*

用户修改实例001的权限:

user:update:001

用户实例001的所有权限:

user:*:001

 

4.授权的编程实现

(1)编程式

(2)注解式

(3)标签式

 

5.授权的实例代码

授权的前提是认证通过,我们拿上面MD5+盐+hash的例子来操作:

CustomerMd5Realm:

doGetAuthorizationInfo方法就是用来授权的

package com.xupeng.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * 自定义Realm实现,加入md5+salt+hash散列
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("------------------------");
        String principal = (String) principalCollection.getPrimaryPrincipal();
        System.out.println("身份信息:"+principal);

        //根据身份信息 用户名获取当前用户的角色信息,以及权限信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");

        //将数据库中查询权限信息复制给权限对象
        simpleAuthorizationInfo.addStringPermission("user:*:01");
        simpleAuthorizationInfo.addStringPermission("product:create");

        return simpleAuthorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //在token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();
        System.out.println(principal);
        //根据身份信息使用jdbc mybatis查询相关数据库
        if("xupeng".equals(principal)){
            //参数1:数据库中正确用户名
            //参数2:数据库中正确MD5密码
            //参数3:提供当前Realm名字
//            SimpleAuthenticationInfo simpleAuthenticationInfo  = new SimpleAuthenticationInfo(
//                    "xupeng",
//                    "250cf8b51c773f3f8dc8b4be867a9a02",
//                    this.getName());

            //参数1:数据库中正确用户名
            //参数2:数据库中正确MD5密码+salt后的密码。如果加了散列次数,就设置散列后的密码
            //参数3:注册时的随机盐
            //参数4:提供当前Realm名字
            SimpleAuthenticationInfo simpleAuthenticationInfo  = new SimpleAuthenticationInfo(
                    "xupeng",
                    "4ca532fb479910d125d72992a3f57b33",
                    ByteSource.Util.bytes("xp"),
                    this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

TestCustomerMd5RealmAuthenticator

package com.xupeng;

import com.xupeng.realm.CustomerMd5Realm;
import com.xupeng.realm.CustomerRealm;
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.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

import java.util.Arrays;

/**
 * 使用自定义的Realm,加入md5+salt+hash散列
 */
public class TestCustomerMd5RealmAuthenticator {
    public static void main(String[] args) {
        //创建SecurityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //注入Realm
        CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();
        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        credentialsMatcher.setHashIterations(1024);
        customerMd5Realm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerMd5Realm);
        //设置安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类来获取subject
        Subject subject = SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xupeng","456");

        try {

            subject.login(token);
            System.out.println(subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("认证失败,认证用户不存在");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("认证失败,密码错误");
        }

        //认证用户进行授权
        if(subject.isAuthenticated()){
            //1.基于角色权限控制
            System.out.println(subject.hasRole("admin"));

            //2.基于多角色的权限控制
            System.out.println(subject.hasRoles(Arrays.asList("admin","user")));

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

            System.out.println("================================");
            //基于权限字符串的访问控制  资源标识符:操作:资源实例标识符
            System.out.println("资源:"+subject.isPermitted("user:update:01"));
            System.out.println("资源:"+subject.isPermitted("product:create:02"));

            //分别具有哪些权限
            boolean[] permitted = subject.isPermitted("user:*:01","order:*:10");
            for(boolean b:permitted){
                System.out.println(b);
            }

            //同时具有哪些权限
            boolean permitAll = subject.isPermittedAll("user:*:01","product:create");
            System.out.println(permitAll);

        }
    }
}

 

七、打赏请求

如果本篇博客对您有所帮助,打赏一点呗,谢谢了呢~

 

 

标签:println,116,shiro,org,apache,import,基本概念,Shiro,subject
来源: https://blog.csdn.net/qq_40594696/article/details/117385023

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有