Shiro 管理多个realm 实现前后台分离

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liiuijkiuu/article/details/53945062

使用shiro 由于公司的业务上的需求前后台公用的一张表,要实现前台用户和后台用户的分离拦截需要书写多个realm 用来验证前台用户还是后台用户。直接上代码
1.书写一个自定的token UsernamePasswordUsertypeToken 继承UsernamePasswordToken 用来判断用户类型UsernamePasswordUsertypeToken 多出一个字段用来区分用户类型

package com.jscredit.zxypt.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

/**
 * 拓展shiro 中的UsernamePasswordToken
 * @author liyaqiang
 *
 */

public class UsernamePasswordUsertypeToken extends UsernamePasswordToken {

        private static final long serialVersionUID = 1L;
        private String usertype ;

        public String getUsertype() {
            return usertype;
        }
        public void setUsertype(String usertype) {
            this.usertype = usertype;
        }

        public UsernamePasswordUsertypeToken(String loginName, String password, String usertype) {

            super(loginName, password);

            this.usertype = usertype;

        }

}

定义前台的验证的realm

public class UserLoginRealm  extends AuthorizingRealm{

    private final Logger LOGGER = LoggerFactory.getLogger(UserLoginRealm.class);

    private static final String ALGORITHM = "MD5";

    @Autowired
    private UserService userService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private UserKzService userKzService;

    public UserLoginRealm(){
        super();
    }


    /**
     * 为当前登录的Subject授予角色和权限 ,该方法的调用时机为需授权资源被访问时
     * 并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
     * 个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
     * 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
     */

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        ShiroLoginUser shiroLoginUser = (ShiroLoginUser) principals.fromRealm(getName()).iterator().next();
        String username = shiroLoginUser.getAccount();
        if (username != null) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

            // 查询用户授权信息
            List<Permission> perList = permissionService.getShiro("O");

            if (perList != null && perList.size() != 0) {
                for (Permission permission : perList) {
                    info.addStringPermission(permission.getPmsnCode());
                }
                return info;
            }
        }
        return null;
    }


    /**
     * 验证前台当前登录的Subject 本例中该方法的调用时机为UserLoginController.login()方法中执行Subject.login()时
     */

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        // 这个authcToken是从userLoginController里面currentUser.login(token)传过来的
                UsernamePasswordUsertypeToken fronttoken = (UsernamePasswordUsertypeToken) authcToken;
                LOGGER.debug("验证当前Subject时获取到token为"
                        + ReflectionToStringBuilder.toString(fronttoken, ToStringStyle.MULTI_LINE_STYLE));
                User users = userService.getUserByName(fronttoken.getUsername());
                UserKz userKz= userKzService.selectByPrimaryKey(users.getUserId());
                String usertype =userKz.getUserType();
                        // Shiro完成对比逻辑,返回和令牌相关的正确的验证信息,第一个参数填登录用户名,第二个参数填合法的登录密码
                    if (users != null&&usertype!="00") {
                        ShiroLoginUser shiroLoginUser = new ShiroLoginUser(users.getUserId(), users.getAccount(),userKz.getUserType());
                        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(shiroLoginUser,users.getPassword(), getName());
                        this.setSession("shiroLoginUser", shiroLoginUser);
                        return authcInfo;
                    } else {
                        throw new AuthenticationException();
                    }
                    // 没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常

    }


    /**
     * 更新用户授权信息缓存.
     */
    public void clearCachedAuthorizationInfo(String principal) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
        clearCachedAuthorizationInfo(principals);
    }

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache != null) {
            for (Object key : cache.keys()) {
                cache.remove(key);
            }
        }
    }

    @PostConstruct
    public void initCredentialsMatcher() {// MD5加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(ALGORITHM);
        setCredentialsMatcher(matcher);
    }

    /**
     * 将一些数据放到ShiroSession中,以便于其它地方使用
     *
     * 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
     */
    private void setSession(Object key, Object value) {
        Subject subject = SecurityUtils.getSubject();
        if (null != subject) {
            Session session = subject.getSession();
            LOGGER.debug("Session默认超时时间为[" + session.getTimeout() + "]毫秒");
            if (null != session) {
                session.setAttribute(key, value);
            }
        }
    }





}

2.后台realm

public class LoginRealm extends AuthorizingRealm {

    private final Logger LOGGER = LoggerFactory.getLogger(LoginRealm.class);

    private static final String ALGORITHM = "MD5";

    @Autowired
    private UserService userService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private UserKzService userKzService;

    public LoginRealm() {
        super();
    }

    /**
     * 验证当前登录的Subject 本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        // 这个authcToken是从LoginController里面currentUser.login(token)传过来的
        UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authcToken;
        LOGGER.debug("验证当前Subject时获取到token为"
                + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
        User users = userService.getUserByName(token.getUsername());
        UserKz userKz =userKzService.selectByPrimaryKey(users.getUserId());
        String usertype =userKz.getUserType();
            // Shiro完成对比逻辑,返回和令牌相关的正确的验证信息,第一个参数填登录用户名,第二个参数填合法的登录密码
            if (users != null&&usertype.equals("00")) {
                ShiroUser shiroUser = new ShiroUser(users.getUserId(), users.getAccount());
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(shiroUser,
                        users.getPassword(), getName());
                this.setSession("shiroUser", shiroUser);
                return authcInfo;
            } else {
                throw new AuthenticationException();
            }
            // 没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常


    }

    /**
     * 为当前登录的Subject授予角色和权限 ,该方法的调用时机为需授权资源被访问时
     * 并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
     * 个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
     * 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        ShiroUser shiroUser = (ShiroUser) principals.fromRealm(getName()).iterator().next();
        String username = shiroUser.getAccount();
        if (username != null) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

            // 查询用户授权信息
            List<Permission> perList = permissionService.getShiro("O");

            if (perList != null && perList.size() != 0) {
                for (Permission permission : perList) {
                    info.addStringPermission(permission.getPmsnCode());
                }
                return info;
            }
        }
        return null;
    }

    /**
     * 更新用户授权信息缓存.
     */
    public void clearCachedAuthorizationInfo(String principal) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
        clearCachedAuthorizationInfo(principals);
    }

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache != null) {
            for (Object key : cache.keys()) {
                cache.remove(key);
            }
        }
    }

    @PostConstruct
    public void initCredentialsMatcher() {// MD5加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(ALGORITHM);
        setCredentialsMatcher(matcher);
    }

    /**
     * 将一些数据放到ShiroSession中,以便于其它地方使用
     *
     * 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
     */
    private void setSession(Object key, Object value) {
        Subject subject = SecurityUtils.getSubject();
        if (null != subject) {
            Session session = subject.getSession();
            LOGGER.debug("Session默认超时时间为[" + session.getTimeout() + "]毫秒");
            if (null != session) {
                session.setAttribute(key, value);
            }
        }
    }
}

3.书写总的管理realm

public class DefautModularRealm extends org.apache.shiro.authc.pam.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 ShiroException("token错误!");  
            }  
            AuthenticationInfo info = null;  
            try {  
                info = realm.getAuthenticationInfo(token);  

                if (info == null) {  
                    throw new ShiroException("token不存在!");  
                }  
            } catch (Exception e) {  
                throw new ShiroException("用户名或者密码错误!");  
            }  
            return info;  
        } 


        /** 
         * 判断登录类型执行操作 
         */  
        @Override  
        protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)throws AuthenticationException {  
            this.assertRealmsConfigured();  
            Realm realm = null;  
            UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authenticationToken;  
           //判断是否是后台用户
            if (token.getUsertype().equals("admin")) {  
                realm = (Realm) this.definedRealms.get("loginRealm");  
            }  
            else{
                realm = (Realm) this.definedRealms.get("userloginRealm");  
            }

            return this.doSingleRealmAuthentication(realm, authenticationToken);  
        }  

        /** 
         * 判断realm是否为空 
         */  
        @Override  
        protected void assertRealmsConfigured() throws IllegalStateException {  
            this.definedRealms = this.getDefinedRealms();  
            if (CollectionUtils.isEmpty(this.definedRealms)) {  
                throw new ShiroException("值传递错误!");  
            }  
        }  

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

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






}

4.书写包含用户类型的token继承 UsernamePasswordToken

public class UsernamePasswordUsertypeToken extends UsernamePasswordToken {

        private static final long serialVersionUID = 1L;
        private String usertype ;

        public String getUsertype() {
            return usertype;
        }
        public void setUsertype(String usertype) {
            this.usertype = usertype;
        }

        public UsernamePasswordUsertypeToken(String loginName, String password, String usertype) {

            super(loginName, password);

            this.usertype = usertype;

        }

}

4.将配置写入到shiro的配置文件中

 <!-- 用户授权信息Cache 缓存在本机内存,不支持集群 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>

    <!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的ShiroDbRealm.java -->
    <bean id="loginRealm" class="com.jscredit.base.shiro.LoginRealm">
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

     <!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证前台用户登录的类为自定义的ShiroDbRealm.java -->
    <bean id="userloginRealm" class="com.jscredit.zxypt.shiro.UserLoginRealm">
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <!--多个realm 的集中管理  -->
    <bean id="defineModularRealmAuthenticator" class=" com.jscredit.zxypt.shiro.DefautModularRealm"> 
        <property name="definedRealms">    
            <map>    
                <entry key="loginRealm" value-ref="loginRealm" />    
                <entry key="userloginRealm" value-ref="userloginRealm" />    
            </map>   
        </property>  
        <property name="authenticationStrategy">    
            <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy" />    
        </property> 
    </bean>   
    <!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session -->
    <!-- 即<property name="sessionMode" value="native"/>,详细说明见官方文档 -->
    <!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
         <property name="authenticator" ref="defineModularRealmAuthenticator" /> 
     <!--    <property name="realm" ref="loginRealm"/> -->
        <property name="realms"  >
            <list>
               <bean id="loginRealm" class="com.jscredit.base.shiro.LoginRealm" /> 
               <bean id="userloginRealm" class="com.jscredit.zxypt.shiro.UserLoginRealm" /> 
            </list>
        </property>
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

这样就可以在controller 中根据前后台传入不同的type 值 ,然后总的realm 会根据当前类型来判断执行哪个realm 进而执行不同的验证。然后我们再在realm中进行前后台用户的区分。进而执行自己的业务逻辑啦

猜你喜欢

转载自blog.csdn.net/liiuijkiuu/article/details/53945062