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

目录

一、权限的基本概念

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的核心架构

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

 

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

1.基本概念

(1)Subject:主体

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

(2)Principal:身份信息

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

(3)credential:凭证信息

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

 

2.认证流程

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

 

3.实例编写

(1)创建项目

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

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

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

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

(2)引入shiro依赖

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

(3)引入shiro配置文件

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

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

 

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

 

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

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

(4)编写控制类

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

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)测试

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

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

 

4.认证源码的解读

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

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

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

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

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

 

5.实例优化:自定义Realm

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

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

 

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("认证失败,密码错误");
        }
    }
}

测试:

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

 

四、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散列实例

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

 

五、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的实现

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

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的例子来操作:

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

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);

        }
    }
}

 

七、打赏请求

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

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

 

 

上一篇:Apache Shiro


下一篇:Mysql数据库版本高低引起的group by问题