03.Shiro自定义Realm实战-无数据库版-梳理shiro自定义realm流程doGetAuthenticationInfo和doGetAuthoriza

410 阅读3分钟

简介:讲解自定义Realm实战基础

  • 步骤:

    • 创建一个类 ,继承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm
    • 重写授权方法 doGetAuthorizationInfo
    • 重写认证方法 doGetAuthenticationInfo
  • 方法:

    • 当用户登陆的时候会调用 doGetAuthenticationInfo
    • 进行权限校验的时候会调用: doGetAuthorizationInfo
  • 对象介绍

    • UsernamePasswordToken : 对应就是 shiro的token中有Principal和Credential

      • UsernamePasswordToken-》HostAuthenticationToken-》AuthenticationToken
        
    • SimpleAuthorizationInfo:代表用户角色权限信息

    • SimpleAuthenticationInfo :代表该用户的认证信息

Apache Shiro自定义Realm实战

现在来小试牛刀一下: 思考一个登录案例,我们有一个登录界面,要检验用户名和密码,如果密码正确才能让后端返回状态码200。我们这个案例如果不使用数据库,我们可以这样使用shiro来实现。

QuickStartTest6.java

package com.lzh;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:QuickStartTest
 * @Date:2023/3/27 下午2:05
 * @Filename:QuickStartTest
 * @Description: Apache Shiro自定义Readl实战
 * @Version:1.0
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;


/**
 * 自定义realm,一定会走AuthorizingRealm类中的doGetAuthenticationInfo方法,
 * 我们既然要自定义realm就要自定义一个CustomRealm继承AuthorizingRealm
 * 然后重写doGetAuthorizationInfo和doGetAuthenticationInfo方法
 *
 */
public class QuickStartTest6 {

    private CustomRealm customRealm = new CustomRealm();

    private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();


    @Before
    public void init(){
        defaultSecurityManager.setRealm(customRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
    }

    @Test
    public void testAuthentication() {

        // 获取当前操作的主体
        Subject subject = SecurityUtils.getSubject();

        // 模拟用户输入的用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");

        // 登录
        subject.login(usernamePasswordToken);

        // 获取验证结果 isAuthenticated() 是否授权,结果是boolen
        System.out.println("认证结果:" + subject.isAuthenticated());

        // 拿到主体标识属性
        System.out.println("getPrincipal认证结果-实际上就是获取登录用户账号" + subject.getPrincipal());
    }
}

自定义realm
CustomRealm.java

自定义realm,一定会走AuthorizingRealm类中的doGetAuthenticationInfo方法, 我们既然要自定义realm就要自定义一个CustomRealm继承AuthorizingRealm 然后重写doGetAuthorizationInfo和doGetAuthenticationInfo方法

package com.lzh;

import cn.hutool.core.util.StrUtil;
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 java.util.HashMap;
import java.util.Map;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:CustomRealm
 * @Date:2023/3/27 下午8:36
 * @Filename:CustomRealm
 * @Description:Apache Shiro自定义Readl实战
 * @Version:1.0
 */
public class CustomRealm extends AuthorizingRealm {

    private final Map<String,String> map = new HashMap<>();

    {
        map.put("zhangsan", "123456");
        map.put("lisi", "123456");
        map.put("wangwu", "123456");
        map.put("jack", "123");
    }

    // 进行权限校验的时候会调用
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("校验权限 doGetAuthorizationInfo");
        return null;

    }
    //进行用户登录的时候会调用
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证 doGetAuthenticationInfo");

        // 从token获取身份信息,token代表用户输入的信息
        String name = (String) token.getPrincipal();

        // 模拟从数据库中,根据用户获取密码
        String pwd = getPwdByUsernameFromDB(name);
        if (StrUtil.isBlank(name)) {
            return null;
        }

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pwd,this.getName());
        return simpleAuthenticationInfo;
    }

    private String getPwdByUsernameFromDB(String name) {
        return map.get(name);
    }
}

Result:

认证 doGetAuthenticationInfo
认证结果:true
getPrincipal认证结果-实际上就是获取登录用户账号jack

完整版:校验登录后角色和权限信息 QuickStartTest6.java


package com.lzh;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:QuickStartTest
 * @Date:2023/3/27 下午2:05
 * @Filename:QuickStartTest
 * @Description: Apache Shiro自定义Readl实战
 * @Version:1.0
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;


/**
 * 自定义realm,一定会走AuthorizingRealm类中的doGetAuthenticationInfo方法,
 * 我们既然要自定义realm就要自定义一个CustomRealm继承AuthorizingRealm
 * 然后重写doGetAuthorizationInfo和doGetAuthenticationInfo方法
 *
 */
public class QuickStartTest6 {




    private CustomRealm customRealm = new CustomRealm();

    private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();


    @Before
    public void init(){
        defaultSecurityManager.setRealm(customRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
    }

    @Test
    public void testAuthentication() {

        // 获取当前操作的主体
        Subject subject = SecurityUtils.getSubject();

        // 模拟用户输入的用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");

        // 登录
        subject.login(usernamePasswordToken);

        // 获取验证结果 isAuthenticated() 是否授权,结果是boolen
        System.out.println("认证结果:" + subject.isAuthenticated());

        // 拿到主体标识属性
        System.out.println("getPrincipal认证结果-实际上就是获取登录用户账号" + subject.getPrincipal());

        // 是否有对应的角色
        System.out.println("是否有user角色:" + subject.hasRole("admin"));

        // 是否有对应的权限
        System.out.println("是否有对应的权限:" + subject.isPermitted("video:find"));
    }
}

自定义realm->CustomRealm.java

package com.lzh;

import cn.hutool.core.util.StrUtil;
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 java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.apache.shiro.web.filter.mgt.DefaultFilter.rest;
import static org.apache.shiro.web.filter.mgt.DefaultFilter.roles;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:CustomRealm
 * @Date:2023/3/27 下午8:36
 * @Filename:CustomRealm
 * @Description:Apache Shiro自定义Realm实战
 * @Version:1.0
 */
public class CustomRealm extends AuthorizingRealm {

    private final Map<String,String> map = new HashMap<>();

    {
        map.put("zhangsan", "123456");
        map.put("lisi", "123456");
        map.put("wangwu", "123456");
        map.put("jack", "123");
    }

    private final Map<String,Set<String>> persistentMap = new HashMap<>();
    {

        // 这里 set1 指的是普通用户权限
        Set<String> set1 = new HashSet<>();

        // 这里set2 指的是管理员权限
        Set<String> set2 = new HashSet<>();

        set1.add("video:find");
        set1.add("video:buy");
        set2.add("video:add");
        set2.add("video:delete");
        persistentMap.put("jack", set1);
        persistentMap.put("lzh", set1);

    }

    private final Map<String,Set<String>> roleMap = new HashMap<>();
    {
        // 这里 set1 指的是普通用户角色
        Set<String> set1 = new HashSet<>();

        // 这里set2 指的是管理员角色
        Set<String> set2 = new HashSet<>();

        set1.add("user");
        set2.add("admin");
        set2.add("user");
        roleMap.put("jack", set1);
        roleMap.put("lzh", set1);
    }

    // 进行权限校验的时候会调用
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("校验权限 doGetAuthorizationInfo");
        // PrincipalCollection principals 是拿到用户从后端获取的账号的信息

        // 模拟从数据库中获取到用户的登录账号
        String name = (String) principals.getPrimaryPrincipal();

        /**
         * 下面这段代码在真实到项目开发中是有问题的,因为数据表中用户是与角色挂钩的;角色是与权限挂钩的。
         * 正确的流程应该是根据用户的登录名查到到角色集合,然后根据角色取查询数据表中到权限集合。
         * 这里是为了简化演示流程,才直接根据登录名查询用户权限集合的!!!
         *
         */

            // 模拟从数据库中获取到用户的权限
            Set<String> permissions = getPermissionsByNameFromDB(name);

            // 模拟从数据库中获取到用的角色
            Set<String> roles = getRolesByNameFromDB(name);


        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }



    //进行用户登录的时候会调用
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证 doGetAuthenticationInfo");

        // 从token获取身份信息,token代表用户输入的信息
        String name = (String) token.getPrincipal();

        // 模拟从数据库中,根据用户获取密码
        String pwd = getPwdByUsernameFromDB(name);
        if (StrUtil.isBlank(name)) {
            return null;
        }

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pwd,this.getName());
        return simpleAuthenticationInfo;
    }

    private String getPwdByUsernameFromDB(String name) {
        return map.get(name);
    }

    /**
     * 模拟用户从数据库获取角色集合
     * @param name
     * @return
     */
    private Set<String> getRolesByNameFromDB(String name) {
        return roleMap.get(name);
    }

    /**
     * 模拟用户从数据库获取权限集合
     * @param name
     * @return
     */
    private Set<String> getPermissionsByNameFromDB(String name) {
        return persistentMap.get(name);
    }
}

Result

认证结果:true
getPrincipal认证结果-实际上就是获取登录用户账号jack
校验权限 doGetAuthorizationInfo
是否有user角色:false
是否有对应的权限:true

shiro是连载文章,欢迎关注我的掘金。