shiro中自定义多realm,并指定realm实现验证

 

我们平常的系统建设中大多数都涉及到两种及以上的登陆方式,例如:手机号码登陆、用户名及密码登陆等,而此时我们是不是就得需要两个realm才能实现? 

通常情况下我们一般都是用的shiro默认的认证器org.apache.shiro.authc.pam.ModularRealmAuthenticator,这里面决定使用realm的是doAuthenticate()方法。而此时我想指定使用我自定义的realm,该怎么去操作呢?请大家看下面的例子:

第一步:

定义一个区分登陆类型的枚举类(你不定义也行,此处定义只是个人习惯的编写方式而已)PHONE为手机号码登陆,USERNAME为用户名跟密码登陆:

public enum LoginType {
    //枚举中对应的值是我们realm注入在spring当中Bean的命名
    PHONE("ShiroUserPhoneRealm"),  USERNAME("MyShiroRealm");

    private String type;

    private LoginType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return this.type.toString();
    }
}

第二步:

创建CustomToken类来继承shiro自带的UsernamePasswordToken类,我们新增了一个loginType字段来判断登陆类型:

public class CustomToken extends UsernamePasswordToken {

    //登录类型,判断是手机号码登陆,还是账号密码登录
    private String loginType;

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

    public String getLoginType() {
        return loginType;
    }

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

第三步:

创建CustomModularRealmAuthenticator类继承ModularRealmAuthenticator,重写doAuthenticate方法用以指定使用我们的自定义realm,并将CustomModularRealmAuthenticator类注入到spring容器中。

@Component
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {
    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)throws AuthenticationException {
        // 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 将authenticationToken强制转换为自定义的CustomToken
        CustomToken customToken = (CustomToken) authenticationToken;
        // 登录类型
        String loginType = customToken.getLoginType();
        // 所有realm
        Collection<Realm> realms = getRealms();
        // 用来存放登陆类型指定的realm
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            //根据我们传入的登陆类型来决定使用realm
            if (realm.getName().contains(loginType))
                typeRealms.add(realm);
        }

        // 判断是单realm还是多realm
        if (typeRealms.size() == 1)
            return doSingleRealmAuthentication(typeRealms.iterator().next(), customToken);
        else
            return doMultiRealmAuthentication(typeRealms, customToken);
    }
}

第四步:

下图是我的LoginType为枚举值USERNAME("MyShiroRealm")是跳转的realm类(该类仅作参考):

/**
*
* loginType为USERNAME
**/
@Service
public class MyShiroRealm extends AuthorizingRealm {

    private Logger log = LoggerFactory.getLogger(MyShiroRealm.class);


    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Session session = SecurityUtils.getSubject().getSession();
        UserVO userVO = (UserVO) session.getAttribute(Constants.USER_SESSION_KEY);
        authorizationInfo.addStringPermissions(userVO.getStringPermissions());
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        // 获取用户的输入帐号和密码
        String userInputAccount = authenticationToken.getPrincipal().toString();

        // 获得用户提交的密码
        String password = "";
        if (authenticationToken.getCredentials() instanceof char[]) {
            password = String.valueOf((char[]) authenticationToken.getCredentials());
        } else {
            password = authenticationToken.getCredentials().toString();
        }

        UserVO userVo = userService.findLoginUser(userInputAccount, IsUserParam.mobile);
        if (userVo == null) {
            throw new AccountException("不存在此用户");
        }
        if (userVo.getIsLocked() == 1){
            throw new AccountException("账户已锁定");
        }
        // 密码是没有解密的,这里是为了跟数据库呼应
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userVo, // 用户名
                userVo.getPassword(), // 密码
                ByteSource.Util.bytes(userVo.getUsername()),
                getName() // realm name
        );
        userVo.setPassword(null);
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(Constants.USER_SESSION_KEY, userVo);
        return authenticationInfo;
    }
}

下图是我的LoginType为枚举值PHONE("ShiroUserPhoneRealm")是跳转的realm类(该类仅作参考):

/**
 *
 * LoginType为PHONE
 **/
@Service
public class ShiroUserPhoneRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Session session = SecurityUtils.getSubject().getSession();
        UserVO userVO = (UserVO) session.getAttribute(Constants.USER_SESSION_KEY);
        authorizationInfo.addStringPermissions(userVO.getStringPermissions());
        return authorizationInfo;
    }

    /**
     * 通过此方法完成认证数据的获取及封装,系统底层会将认证数据传递认证管理器,有认证管理器完成认证操作
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        CustomToken token = null;
        if (authenticationToken instanceof CustomToken) {
            token = (CustomToken) authenticationToken;
        } else {
            return null;
        }
        String userInputAccount = authenticationToken.getPrincipal().toString();

        UserVO userVO = userService.findByUserPhone(userInputAccount, IsUserParam.mobile);
        if (userVO == null) {
            throw new AccountException("不存在此用户");
        }
        if (userVO.getIsLocked() == 1){
            throw new AccountException("账户已锁定");
        }

        userVO.setPassword(null);
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(Constants.USER_SESSION_KEY, userVO);
        return new SimpleAuthenticationInfo(userVO, userInputAccount, getName());
    }
}

 第五步:

配置我们的ShrioConfig,这里有xml、代码两种配置方式我这边使用的是代码的配置方式:

 /**
     *  多方式登陆,只需要继承AuthorizingRealm
     * @return
     */
    @Bean
    public SecurityManager securityManager(Collection<Realm> realms, CustomModularRealmAuthenticator customModularRealmAuthenticator) {
        DefaultWebSecurityManager sManager = new DefaultWebSecurityManager();
        //此处需注意参数装载顺序
        sManager.setAuthenticator(customModularRealmAuthenticator);
        sManager.setRealms(realms);
        return sManager;
    }

如果各位码友需要了解xml的配置方式这里可以去看一下这位大佬写的文章:

https://blog.csdn.net/qq_33099301/article/details/84777016

第六步:

上述配置完成之后我们可以开始使用了,使用方式很简单:

 Subject subject = SecurityUtils.getSubject();
 //引用我们创建的CustomToken填入账号密码及登陆类型
 //这种方式使用的LoginType.USERNAME方式所以他会进入我们的MyShiroRealm
 CustomToken redisToken = new CustomToken(dbUserVO.getUsername(),password,LoginType.USERNAME.toString());
//这种方式使用的LoginType.PHONE方式所以他会进入我们的ShiroUserPhoneRealm
 CustomToken redisToken = new CustomToken(dbUserVO.getUsername(),password,LoginType.PHONE.toString());
 
subject.login(redisToken);

这就实现了shiro指定自定义realm。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值