封面

首先自定义一个DefaultUsernamepasswordToken.java,继承Shiro自带的UsernamePasswordToken,在DefaultUsernamepasswordToken中新加一个属性
private String loginType; 而后生成getter setter;

首先拓展shiro 中的UsernamePasswordToken:

/**
 * 功能描述 TODO
 * 拓展shiro 中的UsernamePasswordToken
 * @author luoyuanxiang
 * {@link UsernamePasswordUsertypeToken}
 * @date  2018/12/25 10:10
 */
public class UsernamePasswordUsertypeToken extends UsernamePasswordToken {

    private static final long serialVersionUID = 1L;
    /**
     * 登陆状态 app用户/管理员用户
     */
    private String loginType;

    public String getLoginType() {
        return loginType;
    }

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

    public UsernamePasswordUsertypeToken(String loginName, String password,boolean rememberMe, String loginType) {

        super(loginName, password,rememberMe);

        this.loginType = loginType;

    }
}

然后自定义一个DefautModularRealm.java,继承 ModularRealmAuthenticator,重写doMultiRealmAuthentication、doSingleRealmAuthentication、doAuthenticate三个方法,还需要定义一个变量,
用来接收传进来的多个Realm。

/**
 * 功能描述 TODO
 * 自定义当使用多realm时管理器
 * @author luoyuanxiang
 * {@link DefautModularRealm}
 * @date  2018/12/25 10:14
 */
public class DefautModularRealm extends ModularRealmAuthenticator{

    private Map<String, Object> definedRealms;
    /**
     * 多个realm实现
     */
    @Override
    protected AuthenticationInfo doMultiRealmAuthentication(
            Collection<Realm> realms, AuthenticationToken token) {
        return super.doMultiRealmAuthentication(realms, token);
    }
    /**
     * 调用单个realm执行操作
     */
    @Override
    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm,
                                                             AuthenticationToken token) {

        // 如果该realms不支持(不能验证)当前token
        if (!realm.supports(token)) {
            throw new MyException("token错误!");
        }
        AuthenticationInfo info = null;
        try {
            info = realm.getAuthenticationInfo(token);
            if (info == null) {
                throw new MyException("token不存在!");
            }
        } catch (Exception e) {
            throw new MyException("系统错误!请联系管理员!");
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) {
        //先判断Realm是否为空
        assertRealmsConfigured();
        //强转为自定义的Token
        UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authenticationToken;
        Realm realm = null;
        if (token.getLoginType().equals(Constant.UserType.SYS_ADMIN.getUserType())) {
            realm = (Realm) this.definedRealms.get("userRealm");
        }
        if (token.getLoginType().equals(Constant.UserType.APP_ADMIN.getUserType())) {
            realm = (Realm) this.definedRealms.get("loginRealm");
        }
        if (realm == null) {
            return null;
        }
        return this.doSingleRealmAuthentication(realm, authenticationToken);
    }

    public Map<String, Object> getDefinedRealms() {
        return definedRealms;
    }

    public void setDefinedRealms(Map<String, Object> definedRealms) {
        this.definedRealms = definedRealms;
    }
}

这里需要注意是 其实 流程是:在给DefaultUsernamepasswordToken赋值时,将loginType设置成相应的参数,然后在doAuthenticate时判断,单个调用对应的Realm。
然后是配置shirconfig类:

/**
 * 功能描述 TODO
 *
 * @author luoyuanxiang
 * {@link ShiroConfig}
 * @date  2018/12/17 13:43
 */
@Slf4j
@Configuration
public class ShiroConfig {

    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager manager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        //配置登录的url和登录成功的url
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        //配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //表示可以匿名访问
        filterChainDefinitionMap.put("/sys/login","anon");
        filterChainDefinitionMap.put("/captcha.jpg","anon");
        filterChainDefinitionMap.put("/assets/**","anon");
        filterChainDefinitionMap.put("/logout","anon");
        filterChainDefinitionMap.put("/wechat/**","anon");
        //表示需要认证才可以访问
        filterChainDefinitionMap.put("/**", "authc");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 配置核心安全事务管理器
     * @return SecurityManager
     */
    @Bean(name="securityManager")
    public SecurityManager securityManager() {

        log.info("--------------shiro已经加载----------------");
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //设置realm.
        securityManager.setAuthenticator(userModularRealmAuthenticator());
        List<Realm> realms = new ArrayList<>(2);
        //添加多个Realm
        realms.add(loginRealm());
        realms.add(userRealm());
        securityManager.setRealms(realms);
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(redisCacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        //注入Cookie(记住我)管理器(remenberMeManager)
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * cookie对象;
     *
     * @return SimpleCookie
     */
    private SimpleCookie rememberMeCookie() {
        // 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // 记住我cookie生效时间30天 ,单位秒。 注释掉,默认永久不过期 2018-07-15
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能
     *
     * @return CookieRememberMeManager
     */
    private CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("1QWLxg+NYmxraMoxAXu/Iw=="));
        return cookieRememberMeManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return RedisManager
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("192.168.2.106");
        redisManager.setPort(6379);
        redisManager.setPassword(null);
        // 超时 秒
        redisManager.setTimeout(6000);
        // 过期时间 秒 默认30天
        redisManager.setExpire(2592000);
        return redisManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return RedisCacheManager
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultWebSessionManager sessionManager() {
        final DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Cookie cookie = new SimpleCookie(java.util.UUID.randomUUID() + ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        sessionManager.setSessionIdCookie(cookie);
        sessionManager.setGlobalSessionTimeout(3600000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * 配置自定义的权限登录器 pc登陆
     * @return UserRealm
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public UserRealm userRealm() {
        return new UserRealm();
    }

    /**
     * 配置自定义的权限登录器 app登陆
     * @return LoginRealm
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public LoginRealm loginRealm() {
        return new LoginRealm();
    }

    /**
     * 配置自定义当使用多realm时管理器
     * */
    @Bean
    public ModularRealmAuthenticator userModularRealmAuthenticator(){
        DefautModularRealm modularRealmAuthenticator = new DefautModularRealm();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        Map<String, Object> definedRealms = new HashMap<>(2);
        definedRealms.put("loginRealm",loginRealm());
        definedRealms.put("userRealm",userRealm());
        modularRealmAuthenticator.setDefinedRealms(definedRealms);
        return modularRealmAuthenticator;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }
}

最后就是配置realm验证了:app登陆验证LoginRealm和系统登陆验证UserRealm 其中的内容就不一一赘述了和之前的配置完全一样
到此就可以实现多用户登陆验证了


书到用时方恨少、事非经过不知难。——陆游



本文结束感谢您的阅读

后端技术   java spring boot
本文作者:忆往昔
版权声明:本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!