SpringBoot集成Shiro,并使用多个Realm

Realm是用来做授权和认证的。

最近项目上有个需求是添加上一个新角色,导致要重写整个登录接口,又不太想修改原来的代码,看着那堆屎山就有点烦,所以就打算直接加一个Realm。

Realm的执行

首先,我们先看Realm是怎么被执行的

Realm是被一个名叫ModularRealmAuthenticator的类执行的

具体细节如下

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        //检验是否有Realm
        this.assertRealmsConfigured();
        //获取所有的Realm
        Collection<Realm> realms = this.getRealms();
        //如果Realm只有1个,就只执行那一个,如果Realm有多个,全部都要执行
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
    }

自定义Token

Shiro自带的UserNamePasswordToken感觉不太够用,因为下面我们要根据Token自带的属性进行区分,所以下面需要扩展UserNamePasswordToken。

/**
 * @Author: Ember
 * @Date: 2021/4/26 17:30
 * @Description:
 */
public class Token extends UsernamePasswordToken {
    /**
    * 用来判断执行哪个Realm
    **/
    private String loginType;

    public Token(String username, String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }



    public String getLoginType() {
        return loginType;
    }

    public void setLoginType(String loginType) {
        this.loginType = loginType;
    }
}

自定义ModularRealmAuthenticator

我们从上面可以看到,Realm的执行是跟ModularRealmAuthenticator密切相关的,所以我们要想改变Realm的执行,就必须扩展ModularRealmAuthenticator,然后重写doAuthenticate方法,最后装配上我们自定义的ModularRealmAuthenticator即可。

我们可以通过参考上面的源代码进行对应重写,本质上只是改变realms集合即可,即将想要执行的realm放到新的realms集合里面,执行该realms集合即可

具体扩展如下

/**
 * @Author: Ember
 * @Date: 2021/4/26 17:36
 * @Description:
 */
public class MyDiyModularRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        Token token = (Token) authenticationToken;
        //判断是否有Realm装配
        this.assertRealmsConfigured();
        Collection<Realm> realms = getRealms();
        //如果不唯一,分类进行
        Collection<Realm> waitChooseRealms = new ArrayList<>();
        for (Realm realm : realms) {
            //遍历所有Realm
            //通过获取Realm的名字,比较token的信息,将想要执行的Realm放到新的Realms组合里面
            if(realm.getName().contains(token.getLoginType())){
                waitChooseRealms.add(realm);
            }
        }
        //与源码一样,如果只有一个,就执行一个
        if (realms.size() == 1){
            return doSingleRealmAuthentication((Realm)waitChooseRealms.iterator().next(), token);
        }
        //与源码一样,如果有多个,就将整个Realms数组放进去
        return doMultiRealmAuthentication(waitChooseRealms,token);
    }
}

创建多个Realm

我自定义的ModularRealmAuthentic是根据Realm的名字来进行区分的,所以,Realm的名字最后不要出现相同的关键字符串,比如下面我要创一个user、manager和merchant的Realm,那么就不可以出现usermerchant这样的Realm。否则,即会进行userRealm,也会进行merchantRealm。

UserRealm

这是原有的

/**
 * @Author: Ember
 * @Date: 2021/4/26 17:36
 * @Description:
 */
public class MyDiyModularRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        Token token = (Token) authenticationToken;
        //判断是否有Realm装配
        this.assertRealmsConfigured();
        Collection<Realm> realms = getRealms();
        //如果不唯一,分类进行
        Collection<Realm> waitChooseRealms = new ArrayList<>();
        for (Realm realm : realms) {
            if(realm.getName().contains(token.getLoginType())){
                waitChooseRealms.add(realm);
            }
        }
        if (realms.size() == 1){
            return doSingleRealmAuthentication((Realm)waitChooseRealms.iterator().next(), token);
        }
        return doMultiRealmAuthentication(waitChooseRealms,token);
    }
}

ManagerRealm和MerchantRealm

这两个Realm只做测试使用,所以就不详细做认证和授权操作了

/**
 * @Author: Ember
 * @Date: 2021/4/26 17:47
 * @Description:
 */
public class ManagerRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("=======进入了ManagerRealm=========");
        return null;
    }
}
/**
 * @Author: Ember
 * @Date: 2021/4/26 17:49
 * @Description:
 */
public class MerchantRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("=========进入了MerchantRealm===========");
        return null;
    }
}

配置ShiroConfig

因为自定义了多个Realm,还有ModularRealmAuthentic,所以这些我们都要装配上

/**
 * @Author: Ember
 * @Date: 2021/4/2 18:05
 * @Description: ShiroConfig
 */
@Configuration
public class MyShiroConfig{

    /**
     * ShiroFactoryBean装配安全管理
     * @param defaultSecurityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //这里要让ThredContext对defaultSecurityManager绑定,否则会报错
        ThreadContext.bind(defaultSecurityManager);
        shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager);
        return shiroFilterFactoryBean;
    }

    /**
     * 安全管理装配Realm
     * @param realm
     * @return
     */
    @Bean
    public DefaultSecurityManager defaultSecurityManager(
            @Qualifier("myRealm") MyUserRealm realm,
            @Qualifier("MerchantRealm") MerchantRealm merchantRealm,
            @Qualifier("ManagerRealm") ManagerRealm managerRealm,
            @Qualifier("authenticator") MyDiyModularRealmAuthenticator authenticator){
        //注意这里要是DefaultWebSecurityManager,否则报错
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(authenticator);
        //装配上Realm
        Collection<Realm> realms = new ArrayList();
        realms.add(realm);
        realms.add(merchantRealm);
        realms.add(managerRealm);
        //securityManager.setRealm(realm);
       // securityManager.setRealm(merchantRealm);
        securityManager.setRealms(realms);
        return securityManager;
    }
    
     /**
     * 自定义的ModularRealmAuthenticator
     * @return
     */
    @Bean(name = "authenticator")
    public MyDiyModularRealmAuthenticator modularRealmAuthenticator(){
        MyDiyModularRealmAuthenticator myDiyModularRealmAuthenticator = new MyDiyModularRealmAuthenticator();
        myDiyModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return myDiyModularRealmAuthenticator;
    }

    /**
     * 自定义Realm
     * @return
     */
    @Bean(name = "MerchantRealm")
    public MerchantRealm merchantRealm(){
        MerchantRealm realm = new MerchantRealm();
        //自定义校验匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置加密算法的散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        //将自定义匹配添加进Realm中
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        //todo 设置缓存

        return realm;

    }

    @Bean(name = "ManagerRealm")
    public ManagerRealm managerRealm(){
        ManagerRealm realm = new ManagerRealm();
        //自定义校验匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置加密算法的散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        //将自定义匹配添加进Realm中
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        //todo 设置缓存

        return realm;

    }
    @Bean(name = "myRealm")
    public MyUserRealm realm(){
        MyUserRealm realm = new MyUserRealm();
        //自定义校验匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置加密算法的散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        //将自定义匹配添加进Realm中
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        //todo 设置缓存

        return realm;

    }
}

如果没有让ThreadContext绑定securityManager,会报错,如下图

在这里插入图片描述

测试

这样我们就配置好了,下面进行单元测试一下

@SpringBootTest
class DodudiApplicationTests {
    @Test
    void contextLoads() {
        Subject subject = SecurityUtils.getSubject();
        Token token = new Token("123","123","Merchant");
        subject.login(token);
    }
}

在这里插入图片描述
可以看到进入了MerchantRealm

下面将Merchant修改为Manager

在这里插入图片描述
正确进入ManagerRealm。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值