其他分享
首页 > 其他分享> > 2、Shiro认证

2、Shiro认证

作者:互联网

一、shiro的认证:

1、身份认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。

2、shiro中认证的关键对象

Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;

3、Principal:身份信息

是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。

4、credential:凭证信息

是只有主体自己知道的安全信息,如密码、证书等。

二、认证流程

2.1 认证案例:

1、创建maven项目,引入依赖。
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.9.0</version>
</dependency>
2.、引入shiro配置文件

配置文件:名称随意,以 .ini 结尾,放在 resources 目录下

注意在实际的项目开发中并不会使用这种方式,这种方法可以用来初学时练手

创建shiro.ini文件,里面内容为:

[users]
xiaowang=123456
xiaoming=123
xiaozhang=2345
3、开始入门案例编写
package com.laity.test;

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;

/**
 * @author: laity
 * @date: 2022/5/9 7:48 下午
 * @description:入门shiro案例
 */
public class TestAuthenticator {

    public static void main(String[] args) {

        //创建安全管理器对象 --	直接用SecurityManager的实现类进行创建对象
        //SecurityManager securityManager = new DefaultSecurityManager();
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

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

        //SecurityUtils 给全局的安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

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

        //创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaowang","123");

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

    }
}
4、常见类型错误

DisabledAccountException(帐号被禁用)
LockedAccountException (帐号被锁定)
ExcessiveAttemptsException(登录失败次数过多)
ExpiredCredentialsException(凭证过期)

5、自定义Realm

解读源码可知:

认证:

​ 1. 最终执行用户名比较是 在SimpleAccountRealm类 的 doGetAuthenticationInfo 方法中完成用户名校验

​ 2. 最终密码校验是在 AuthenticatingRealm类 的 assertCredentialsMatch方法 中

总结:

AuthenticatingRealm 认证realm doGetAuthenticationInf

AuthorizingRealm 授权realm doGetAuthorizationInfo

自定义Realm的作用:放弃使用.ini文件,使用数据库查询

上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。

通过查看源码得知,最终使用的是SimpleAccountRealm两个方法实现的认证和授权

源码部分代码:

public class SimpleAccountRealm extends AuthorizingRealm {
		//.......省略
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
}

6、自定义realm
package com.laity.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;

/**
 * @author: laity
 * @date: 2022/5/9 8:36 下午
 * @description:自定义realm 将认证的/授权的数据的来源转为数据的实现
 * 							假设数据是从数据中取到的,为了去掉ini配置文件,演示自定义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);
        //1、根据用户名查询数据,设置假数据
        if ("xiaowang".equals(principal)){
            //参数1:返回数据库中正确的用户名,参数2:返回数据库中正确的密码
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123456",this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}
7、使用自定义Realm认证测试方法
package com.laity.test;

import com.laity.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;

/**
 * @author: laity
 * @date: 2022/5/9 8:40 下午
 * @description:测试自定realm
 */
public class TestCustomerAuthenticator {

    public static void main(String[] args) {

        //创建SecurityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //设置自定以Realm
        defaultSecurityManager.setRealm(new CustomerRealm());

        //SecurityUtils 给全局的安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        //通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();

        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaowang","123456");

        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中的MD5

补充:MD5算法

作用:一般用来加密或者签名(校验)

特点:MD5算法不可逆如何内容相同无论执行多少次md5生成结果始终是一致

​ 网络上提供的MD5在线解密一般是用穷举的方法(防止穷举的办法是加盐,shiro为了保证安全,进行了在hash)

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

1、MD5的基本使用:

package com.laity.test;

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

import java.nio.charset.StandardCharsets;

/**
 * @author: laity
 * @date: 2022/5/9 9:19 下午
 * @description: 测试shiro中的MD5加密
 */
public class TestShiroMD5 {

    public static void main(String[] args) {
       /** 错误用法   313233

          Md5Hash md5Hash = new Md5Hash();
          md5Hash.setBytes("123".getBytes()); //不能直接这样使用,输出的不是加密字符串,而是313233
          System.out.println(md5Hash.toHex());
        */

        Md5Hash md5Hash = new Md5Hash("123456");
        System.out.println(md5Hash.toHex());

        //使用MD5加盐操作
        Md5Hash md5HashSalt = new Md5Hash("123456","XO*7ps");
        System.out.println(md5HashSalt.toHex());

        //使用MD5+salt+hash散列  1024/2048
        Md5Hash md5Hash1 = new Md5Hash("123456", "XO*7ps", 1024);
        System.out.println(md5Hash1);

    }
}

2、自定义md5+salt的realm

package com.laity.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;

/**
 * @author: laity
 * @date: 2022/5/9 9:29 下午
 * @description: 使用自定义real加入md5+salt+hash
 */
public class CustomerRealmMD5 extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

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

        if("xiaowang".equals(principal)){
            return new SimpleAuthenticationInfo(principal,
                    "81c97817c4e4db0baa3ebd97ad2afebb",
                    ByteSource.Util.bytes("XO*7ps"),
                    this.getName());
        }
        return null;
    }
}

测试自定义md5+salt的realm

package com.laity.test;

import com.laity.realm.CustomerRealmMD5;
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.crypto.hash.Hash;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

/**
 * @author: laity
 * @date: 2022/5/9 9:31 下午
 * @description: 测试加入MD5的Realm
 */
public class TestCustomerAuthenticatorMD5 {

    public static void main(String[] args) {

        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

  
        CustomerRealmMD5 realm =new CustomerRealmMD5();

        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //使用算法
        credentialsMatcher.setHashAlgorithmName("md5");
        realm.setCredentialsMatcher(credentialsMatcher);
      
        //注入realm
        defaultSecurityManager.setRealm(realm);
        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全类获取subject
        Subject subject = SecurityUtils.getSubject();

        //获取token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaowang", "123456");

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

}

测试自定义md5+salt+hash的realm

package com.laity.test;

import com.laity.realm.CustomerRealmMD5;
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.crypto.hash.Hash;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;

/**
 * @author: laity
 * @date: 2022/5/9 9:31 下午
 * @description: 测试加入MD5的Realm
 */
public class TestCustomerAuthenticatorMD5 {

    public static void main(String[] args) {

        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //注入realm
        CustomerRealmMD5 realm =new CustomerRealmMD5();

        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //使用算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列次数
        credentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(credentialsMatcher);

        defaultSecurityManager.setRealm(realm);
        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全类获取subject
        Subject subject = SecurityUtils.getSubject();

        //获取token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaowang", "123456");

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

}

加入hash的验证与未加入hash的区别是

在使用hash的里面加入了hash的散列次数

credentialsMatcher.setHashIterations(1024);

标签:realm,认证,Shiro,org,apache,import,shiro
来源: https://www.cnblogs.com/cmsdnbog/p/16254834.html