分布式统一权限系统
设计
shiro-mgt:用户、角色、资源的定义,资源对应网关的定义,动态链的定义
shiro鉴权服务集群:会话信息、Realm信息
dubbo-app集群:实际业务相关
模块依赖
Springboot-shiro-gateway-handler功能:
- dubbo业务服务转换http通讯
- 认证与鉴权服务化消费者
- 生成业务服务化消费者
springboot-shiro-producer功能:
springboot-shiro-mgt功能:
springboot-shiro-dubbo-app-handler功能:
动态过滤器链
实现动态过滤器链,我们需要保证以下几个特性:
1、持久化:原有的properties内容放入数据库,
2、有序性:因过滤器链有序加载的特性,读取过滤器链的时保证其有序性
3、服务化:过滤器链的服务做成dubbo服务,做到集中式管理
4、同步性:不同业务系统对于过滤器链的加载需要同步
5、热加载:过滤器链修改之后,各个业务系统不需要重启服务,以达到热加载的目的
实现持久化、有序化
过滤器链包含的字段:- package com.itheima.shiro.vo;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import org.apache.commons.lang3.builder.ToStringBuilder;import org.apache.commons.lang3.builder.ToStringStyle;import java.io.Serializable;@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class FilterChainVo implements Serializable { /** * 主键 */ private String id; /** * 描述 */ private String urlName; /** * 路径 */ private String url; /** * 拦截器名称 */ private String filterName; /** * 所需角色,可省略,用逗号分隔 */ private String roles; /** * 所需权限,可省略,用逗号分隔 */ private String permissions; /** * 排序 */ private Integer sortNo; private String enableFlag; private static final long serialVersionUID = 1L; @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); }}
复制代码 实现服务化
目的:使所有的服务器链查询请求都通过这个服务来调用。- package com.itheima.shiro.face;import com.itheima.shiro.vo.FilterChainVo;import java.util.List;/** * @Description:过滤器链服务化接口 */public interface FilterChainFace { /** * @Description 查询所有有效的过滤器链 */ List findFilterChainList();}
复制代码- package com.itheima.shiro.faceImpl;import com.itheima.shiro.face.FilterChainFace;import com.itheima.shiro.pojo.FilterChain;import com.itheima.shiro.service.FilterChainService;import com.itheima.shiro.utils.BeanConv;import com.itheima.shiro.utils.EmptyUtil;import com.itheima.shiro.vo.FilterChainVo;import org.apache.dubbo.config.annotation.Service;import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/** * @Description:过滤器链服务化接口 */@Service(version = "1.0.0",retries = 3,timeout = 5000)public class FilterChainFaceImpl implements FilterChainFace { @Autowired FilterChainService filterChainService; @Override public List findFilterChainList() { List filterChainList = filterChainService.findFilterChainList(); if (!EmptyUtil.isNullOrEmpty(filterChainList)){ // 将数据库返回的数据结构转换成标准的过滤器链格式 return BeanConv.toBeanList(filterChainList, FilterChainVo.class); } return null; }}
复制代码 实现同步性
先读取数据库的过滤器链,调用上面filterChainFace服务
- FilterChainBridgeService:过滤器链桥接器service接口层
- FilterChainBridgeServiceImpl:过滤器链桥接器service接口层实现
将过滤器链加载到shiro中
- ShiroFilerChainService:shiro过滤器链服务加载接口
- ShiroFilerChainService:shiro过滤器链服务加载接口实现
- package com.itheima.shiro.core.bridge;import com.itheima.shiro.vo.FilterChainVo;import javax.servlet.FilterChain;import java.util.List;/** * @Description:自定义过滤器桥接接口 */public interface FilterChainBridgeService { /** * @Description 查询所有有效的过滤器链 */ List findFilterChainList();}
复制代码- package com.itheima.shiro.client;import com.itheima.shiro.core.bridge.FilterChainBridgeService;import com.itheima.shiro.face.FilterChainFace;import com.itheima.shiro.vo.FilterChainVo;import org.apache.dubbo.config.annotation.Reference;import org.springframework.stereotype.Service;import java.util.List;/** * @Description:自定义过滤器桥接接口实现 */@Service("filterChainBridgeService")public class FilterChainBridgeServiceImpl implements FilterChainBridgeService { @Reference(version = "1.0.0") private FilterChainFace filterChainFace; @Override public List findFilterChainList() { return filterChainFace.findFilterChainList(); }}
复制代码- package com.itheima.shiro.service;import com.itheima.shiro.vo.FilterChainVo;import java.util.List;/** * @Description:过滤器同步接口 */public interface ShiroFilerChainService { /** * @Description 启动时,启动定时器,每隔2分钟动态加载数据库里面的过滤器链 */ void init(); /** * @Description 使用DefaultFilterChainManager的addToChain方法构建过滤器链 */ void initFilterChains(List filterChainVos);}
复制代码- package com.itheima.shiro.service.impl;import com.itheima.shiro.core.bridge.FilterChainBridgeService;import com.itheima.shiro.core.impl.CustomDefaultFilterChainManager;import com.itheima.shiro.service.ShiroFilerChainService;import com.itheima.shiro.vo.FilterChainVo;import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;import org.apache.shiro.web.filter.mgt.NamedFilterList;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * @Description:过滤器同步接口实现 */@Service("shiroFilerChainService")public class ShiroFilerChainServiceImpl implements ShiroFilerChainService { //注入过滤器链管理者 @Autowired private CustomDefaultFilterChainManager defaultFilterChainManager; //使用桥接器读取数据库内的过滤器链 @Autowired FilterChainBridgeService filterChainBridgeService; private Map defaultFilterChains; //定时器 private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); // 每个服务器启动都会加载这个方法 @Override @PostConstruct public void init() { defaultFilterChains = new LinkedHashMap(); // 延时为0,每120秒执行一次 executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { initFilterChains(filterChainBridgeService.findFilterChainList()) ; } }, 0, 120, TimeUnit.SECONDS); } @Override public void initFilterChains(List filterChainVos) { //1、首先删除以前老的filter,构建默认的过滤器链 defaultFilterChainManager.getFilterChains().clear(); //2、加载过滤器链 for (FilterChainVo urlFilterVo : filterChainVos) { String url = urlFilterVo.getUrl(); String filterName = urlFilterVo.getFilterName(); String[] filterNames = filterName.split(","); for (String name : filterNames) { //注册所有filter,包含自定义的过滤器 switch(name){ case "anon": defaultFilterChainManager.addToChain(url, name); break; case "authc": defaultFilterChainManager.addToChain(url, name); break; case "roles": defaultFilterChainManager.addToChain(url, name, urlFilterVo.getRoles()); break; case "perms": defaultFilterChainManager.addToChain(url, name,urlFilterVo.getPermissions()); break; case "role-or": defaultFilterChainManager.addToChain(url, name,urlFilterVo.getRoles()); break; case "kicked-out": defaultFilterChainManager.addToChain(url, name); break; case "jwt-authc": defaultFilterChainManager.addToChain(url, name); break; case "jwt-roles": defaultFilterChainManager.addToChain(url, name, urlFilterVo.getRoles()); break; case "jwt-perms": defaultFilterChainManager.addToChain(url, name,urlFilterVo.getPermissions()); break; default: break; } } } }}
复制代码 实现热加载
为了实现热加载我们需要定义以下3个类:
- CustomDefaultFilterChainManager:自定义的默认过滤器链管理者
- CustomPathMatchingFilterChainResolver:自定义的路径匹配过滤器链解析器
- CustomShiroFilterFactoryBean:自定义shiro过滤器工厂bean
CustomDefaultFilterChainManager:主要是把原来对象的创建交于spring容器,同时指定过滤器,然后构建过滤器链- package com.itheima.shiro.core.impl;import org.apache.shiro.util.StringUtils;import org.apache.shiro.web.filter.AccessControlFilter;import org.apache.shiro.web.filter.authc.AuthenticationFilter;import org.apache.shiro.web.filter.authz.AuthorizationFilter;import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;import org.apache.shiro.web.filter.mgt.NamedFilterList;import javax.annotation.PostConstruct;import javax.servlet.Filter;import java.util.LinkedHashMap;import java.util.Map;/** * @Description:自定义过滤器链管理者 */public class CustomDefaultFilterChainManager extends DefaultFilterChainManager { //登录地址 private String loginUrl; //登录成功之后跳转地址 private String successUrl; //未授权跳转地址 private String unauthorizedUrl; /** * @Description 构建过滤器和过滤器链的map对象,加载默认的过滤器链 */ public CustomDefaultFilterChainManager() { setFilters(new LinkedHashMap()); setFilterChains(new LinkedHashMap()); addDefaultFilters(true); } @PostConstruct public void init(){ Map defaultFilters = getFilters(); //apply global settings if necessary: for (Filter filter : defaultFilters.values()) { applyGlobalPropertiesIfNecessary(filter); } } private void applyGlobalPropertiesIfNecessary(Filter filter) { applyLoginUrlIfNecessary(filter); applySuccessUrlIfNecessary(filter); applyUnauthorizedUrlIfNecessary(filter); } private void applyLoginUrlIfNecessary(Filter filter) { String loginUrl = getLoginUrl(); if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) { AccessControlFilter acFilter = (AccessControlFilter) filter; //only apply the login url if they haven't explicitly configured one already: String existingLoginUrl = acFilter.getLoginUrl(); if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) { acFilter.setLoginUrl(loginUrl); } } } private void applySuccessUrlIfNecessary(Filter filter) { String successUrl = getSuccessUrl(); if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) { AuthenticationFilter authcFilter = (AuthenticationFilter) filter; //only apply the successUrl if they haven't explicitly configured one already: String existingSuccessUrl = authcFilter.getSuccessUrl(); if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) { authcFilter.setSuccessUrl(successUrl); } } } private void applyUnauthorizedUrlIfNecessary(Filter filter) { String unauthorizedUrl = getUnauthorizedUrl(); if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) { AuthorizationFilter authzFilter = (AuthorizationFilter) filter; //only apply the unauthorizedUrl if they haven't explicitly configured one already: String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl(); if (existingUnauthorizedUrl == null) { authzFilter.setUnauthorizedUrl(unauthorizedUrl); } } } /** * @Description 加载自定义过滤器的对象 * @param customFilters 自定义过滤器的map架构 * @return */ public void setCustomFilters(Map customFilters){ for (Map.Entry entry : customFilters.entrySet()) { addFilter(entry.getKey(), entry.getValue(), false); } } /** * @Description 不再使用shiro容器去构建,而是交于spring去构建 */ @Override protected void initFilter(Filter filter) { } public String getLoginUrl() { return loginUrl; } public void setLoginUrl(String loginUrl) { this.loginUrl = loginUrl; } public String getSuccessUrl() { return successUrl; } public void setSuccessUrl(String successUrl) { this.successUrl = successUrl; } public String getUnauthorizedUrl() { return unauthorizedUrl; } public void setUnauthorizedUrl(String unauthorizedUrl) { this.unauthorizedUrl = unauthorizedUrl; }}
复制代码 CustomPathMatchingFilterChainResolver这里主要核心内容是:指定使用过滤器链管理器为自己定的过滤器管理器- package com.itheima.shiro.core.impl;import lombok.extern.log4j.Log4j2;import org.apache.shiro.web.filter.mgt.FilterChainManager;import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;import javax.servlet.FilterChain;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;/** * @Description:自定义路径解析器 */@Log4j2public class CustomPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver { private CustomDefaultFilterChainManager defaultFilterChainManager; public CustomDefaultFilterChainManager getDefaultFilterChainManager() { return defaultFilterChainManager; } public void setDefaultFilterChainManager(CustomDefaultFilterChainManager defaultFilterChainManager) { this.defaultFilterChainManager = defaultFilterChainManager; } public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { //不在使用默认的过滤器链管理者,而是使用自定义的过滤器管理者 FilterChainManager filterChainManager = getDefaultFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } String requestURI = getPathWithinApplication(request); //the 'chain names' in this implementation are actually path patterns defined by the user. We just use them //as the chain name for the FilterChainManager's requirements for (String pathPattern : filterChainManager.getChainNames()) { // If the path does match, then pass on to the subclass implementation for specific checks: if (pathMatches(pathPattern, requestURI)) { if (log.isTraceEnabled()) { log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " + "Utilizing corresponding filter chain..."); } return filterChainManager.proxy(originalChain, pathPattern); } } return null; }}
复制代码- package com.itheima.shiro.core.impl;import lombok.extern.log4j.Log4j2;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.filter.mgt.FilterChainManager;import org.apache.shiro.web.filter.mgt.FilterChainResolver;import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;import org.apache.shiro.web.mgt.WebSecurityManager;import org.apache.shiro.web.servlet.AbstractShiroFilter;import org.springframework.beans.factory.BeanInitializationException;/** * @Description:重写shirod过滤器工厂 */@Log4j2public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean { PathMatchingFilterChainResolver chainResolver ; public void setChainResolver(PathMatchingFilterChainResolver chainResolver) { this.chainResolver = chainResolver; } protected AbstractShiroFilter createInstance() throws Exception { log.debug("Creating Shiro Filter instance."); SecurityManager securityManager = getSecurityManager(); if (securityManager == null) { String msg = "SecurityManager property must be set."; throw new BeanInitializationException(msg); } if (!(securityManager instanceof WebSecurityManager)) { String msg = "The security manager does not implement the WebSecurityManager interface."; throw new BeanInitializationException(msg); } FilterChainManager manager = createFilterChainManager(); return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver); } private static final class SpringShiroFilter extends AbstractShiroFilter { protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { super(); if (webSecurityManager == null) { throw new IllegalArgumentException("WebSecurityManager property cannot be null."); } setSecurityManager(webSecurityManager); if (resolver != null) { setFilterChainResolver(resolver); } } }}
复制代码 shiroConfig修改
- package com.itheima.shiro.config;import com.itheima.shiro.constant.SuperConstant;import com.itheima.shiro.core.ShiroDbRealm;import com.itheima.shiro.core.filter.*;import com.itheima.shiro.core.impl.*;import com.itheima.shiro.properties.PropertiesUtil;import lombok.extern.log4j.Log4j2;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.session.mgt.eis.SessionDAO;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.apache.shiro.web.servlet.SimpleCookie;import org.redisson.Redisson;import org.redisson.api.RedissonClient;import org.redisson.config.Config;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import javax.servlet.Filter;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;/** * @Description 权限配置类 */@Configuration@ComponentScan(basePackages = {"com.itheima.shiro.core"})@EnableConfigurationProperties({ShiroRedisProperties.class})@Log4j2public class ShiroConfig { @Autowired private ShiroRedisProperties shiroRedisProperties; @Autowired JwtTokenManager jwtTokenManager; /** * @Description redission客户端 */ @Bean("redissonClientForShiro") public RedissonClient redissonClient() { log.info("=====初始化redissonClientForShiro开始======"); String[] nodeList = shiroRedisProperties.getNodes().split(","); Config config = new Config(); if (nodeList.length == 1) { config.useSingleServer().setAddress(nodeList[0]) .setConnectTimeout(shiroRedisProperties.getConnectTimeout()) .setConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize()) .setConnectionPoolSize(shiroRedisProperties.getConnectPoolSize()).setTimeout(shiroRedisProperties.getTimeout()); } else { config.useClusterServers().addNodeAddress(nodeList) .setConnectTimeout(shiroRedisProperties.getConnectTimeout()) .setMasterConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize()) .setMasterConnectionPoolSize(shiroRedisProperties.getConnectPoolSize()).setTimeout(shiroRedisProperties.getTimeout()); } RedissonClient redissonClient = Redisson.create(config); log.info("=====初始化redissonClientForShiro完成======"); return redissonClient; } /** * @Description 创建cookie对象 */ @Bean(name="sessionIdCookie") public SimpleCookie simpleCookie(){ SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setName("ShiroSession"); return simpleCookie; } /** * @Description 权限管理器 * @param * @return */ @Bean(name="securityManager") public DefaultWebSecurityManager defaultWebSecurityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroDbRealm()); securityManager.setSessionManager(shiroSessionManager()); return securityManager; } /** * @Description 密码比较器 */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher (){ RetryLimitCredentialsMatcher matcher = new RetryLimitCredentialsMatcher(SuperConstant.HASH_ALGORITHM,redissonClient()); matcher.setHashIterations(SuperConstant.HASH_INTERATIONS); return matcher; } /** * @Description 自定义RealmImpl */ @Bean(name="shiroDbRealm") public ShiroDbRealm shiroDbRealm(){ ShiroDbRealm shiroDbRealm =new ShiroDbRealmImpl(); shiroDbRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return shiroDbRealm; } /** * @Description 自定义session会话存储的实现类 ,使用Redis来存储共享session,达到分布式部署目的 */ @Bean("redisSessionDao") public SessionDAO redisSessionDao(){ RedisSessionDao sessionDAO = new RedisSessionDao(); sessionDAO.setGlobalSessionTimeout(shiroRedisProperties.getGlobalSessionTimeout()); return sessionDAO; } /** * @Description 会话管理器 */ @Bean(name="sessionManager") public ShiroSessionManager shiroSessionManager(){ ShiroSessionManager sessionManager = new ShiroSessionManager(); sessionManager.setSessionDAO(redisSessionDao()); sessionManager.setSessionValidationSchedulerEnabled(false); sessionManager.setSessionIdCookieEnabled(true); sessionManager.setSessionIdCookie(simpleCookie()); sessionManager.setGlobalSessionTimeout(shiroRedisProperties.getGlobalSessionTimeout()); return sessionManager; } /** * @Description 保证实现了Shiro内部lifecycle函数的bean执行 */ @Bean(name = "lifecycleBeanPostProcessor") public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * @Description AOP式方法级权限检查 */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /** * @Description 配合DefaultAdvisorAutoProxyCreator事项注解权限校验 */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(defaultWebSecurityManager()); return new AuthorizationAttributeSourceAdvisor(); } /** * @Description 自定义过滤器定义 */ private Map filters() { Map map = new HashMap(); map.put("role-or", new RolesOrAuthorizationFilter()); map.put("kicked-out", new KickedOutAuthorizationFilter(redissonClient(), redisSessionDao(), shiroSessionManager())); map.put("jwt-authc", new JwtAuthcFilter(jwtTokenManager)); map.put("jwt-perms", new JwtPermsFilter()); map.put("jwt-roles", new JwtRolesFilter()); return map; } // ===========================以下是修改部分================================ @Bean public CustomDefaultFilterChainManager defaultFilterChainManager(){ CustomDefaultFilterChainManager defaultFilterChainManager = new CustomDefaultFilterChainManager(); defaultFilterChainManager.setCustomFilters(filters()); defaultFilterChainManager.setLoginUrl("/login"); defaultFilterChainManager.setSuccessUrl("/home"); defaultFilterChainManager.setUnauthorizedUrl("/login"); return defaultFilterChainManager; } @Bean public CustomPathMatchingFilterChainResolver chainResolver(){ CustomPathMatchingFilterChainResolver chainResolver = new CustomPathMatchingFilterChainResolver(); //指定过滤器管理者 chainResolver.setDefaultFilterChainManager(defaultFilterChainManager()); return chainResolver; } /** * @Description Shiro过滤器 */ @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(){ CustomShiroFilterFactoryBean shiroFilter = new CustomShiroFilterFactoryBean(); shiroFilter.setChainResolver(chainResolver()); shiroFilter.setSecurityManager(defaultWebSecurityManager()); return shiroFilter; }}
复制代码 shiro-client客户端
shiro-client作为jar的依赖,满足以下需求:
1、非侵入式:使用者只需要对jar依赖和做少量的配置,就可以达到统一鉴权的目标
2、可扩展性:用户除使用提供的过滤器外,可以轻松安自己的业务区定义过滤器
3、集中式管理:依赖jar之后,shiro-mgt后台可以同时管控多个平台的权限的认证、鉴权、及动态配置过滤器链
依赖关系
原理分析
springboot-shiro-framework-client项目向上继承了springboot-shiro-framework-core项目,springboot-shiro-framework-core是主要实现认证、鉴权、过滤器定义、会话统一、realm缓存的核心项目。
springboot-shiro-framework-client项目以jar的方式被需要做权限控制的gateway项目所依赖,再由gateway通过对springboot-shiro-producer的dubbo消费,以达到统一认证、鉴权
springboot-shiro-framework-client模块实现了springboot-shiro-framework-core接口的3个类:
UserBridgeServiceImpl:提供用户基本资源操作的业务实现
FilterChainBridgeServiceImpl:提供过滤器链接口的查询
ResourceBridgeServiceImpl:提供资源查询- package com.itheima.shiro.client;import com.itheima.shiro.core.bridge.FilterChainBridgeService;import com.itheima.shiro.face.FilterChainFace;import com.itheima.shiro.vo.FilterChainVo;import org.apache.dubbo.config.annotation.Reference;import org.springframework.stereotype.Service;import java.util.List;/** * @Description:自定义过滤器桥接接口实现 */@Service("filterChainBridgeService")public class FilterChainBridgeServiceImpl implements FilterChainBridgeService { @Reference(version = "1.0.0") private FilterChainFace filterChainFace; @Override public List findFilterChainList() { return filterChainFace.findFilterChainList(); }}
复制代码- package com.itheima.shiro.client;import com.itheima.shiro.core.bridge.ResourceBridgeService;import com.itheima.shiro.face.ResourceAdapterFace;import com.itheima.shiro.vo.ResourceVo;import org.apache.dubbo.config.annotation.Reference;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.util.List;/** * @Description:网关资源桥接器接口实现 */@Component("resourceBridgeService")public class ResourceBridgeServiceImpl implements ResourceBridgeService { @Value("${itheima.resource.systemcode}") private String systemCode; @Reference(version = "1.0.0") ResourceAdapterFace resourceAdapterFace; @Override public List findValidResourceVoAll(String systemCode) { return resourceAdapterFace.findValidResourceVoAll(systemCode); }}
复制代码- package com.itheima.shiro.client;import com.itheima.shiro.constant.CacheConstant;import com.itheima.shiro.core.SimpleCacheService;import com.itheima.shiro.core.base.ShiroUser;import com.itheima.shiro.core.base.SimpleMapCache;import com.itheima.shiro.core.base.SimpleToken;import com.itheima.shiro.core.bridge.UserBridgeService;import com.itheima.shiro.face.UserAdapterFace;import com.itheima.shiro.utils.BeanConv;import com.itheima.shiro.utils.EmptyUtil;import com.itheima.shiro.utils.ShiroUserUtil;import com.itheima.shiro.vo.ResourceVo;import com.itheima.shiro.vo.RoleVo;import com.itheima.shiro.vo.UserVo;import lombok.extern.slf4j.Slf4j;import org.apache.dubbo.config.annotation.Reference;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.util.ByteSource;import org.redisson.api.RBucket;import org.redisson.api.RedissonClient;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.TimeUnit;/** * @Description 权限桥接器 */@Slf4j@Component("userBridgeService")public class UserBridgeServiceImpl implements UserBridgeService { @Reference(version = "1.0.0") private UserAdapterFace userAdapterFace; @Autowired private SimpleCacheService simpleCacheService; @javax.annotation.Resource(name = "redissonClientForShiro") private RedissonClient redissonClient; public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken,String realmName) { SimpleToken token = (SimpleToken)authcToken; UserVo user = this.findUserByLoginName(token.getUsername()); if(EmptyUtil.isNullOrEmpty(user)){ throw new UnknownAccountException("账号不存在"); } ShiroUser shiroUser = BeanConv.toBean(user, ShiroUser.class); String sessionId = ShiroUserUtil.getShiroSessionId(); String cacheKeyResourcesIds = CacheConstant.RESOURCES_KEY_IDS+sessionId; shiroUser.setResourceIds(this.findResourcesIdsList(cacheKeyResourcesIds,user.getId())); String salt = user.getSalt(); String password = user.getPassWord(); return new SimpleAuthenticationInfo(shiroUser, password, ByteSource.Util.bytes(salt), realmName); } @Override public SimpleAuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser) { UserVo user = BeanConv.toBean(shiroUser, UserVo.class); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); String sessionId = ShiroUserUtil.getShiroSessionId(); //查询用户拥有的角色 String cacheKeyRole = CacheConstant.ROLE_KEY + sessionId; info.addRoles(this.findRoleList(cacheKeyRole, user.getId())); //查询用户拥有的资源 String cacheKeyResources = CacheConstant.RESOURCES_KEY + sessionId; info.addStringPermissions(this.findResourcesList(cacheKeyResources, user.getId())); return info; } @Override public List findRoleList(String cacheKeyRole, String userId) { List roles = new ArrayList(); if (simpleCacheService.getCache(cacheKeyRole) != null) { roles = (List) simpleCacheService.getCache(cacheKeyRole).get(cacheKeyRole); } else { roles = userAdapterFace.findRoleByUserId(userId); if (roles.size() > 0) { //用户角色存放到map Map mapRole = new HashMap(); mapRole.put(cacheKeyRole, roles); //新建SimpleMapCache实例并放入缓存管理器 SimpleMapCache cacheRole = new SimpleMapCache(cacheKeyRole, mapRole); simpleCacheService.creatCache(cacheKeyRole, cacheRole); } } List rolesLabel = new ArrayList(); for (RoleVo role : roles) { rolesLabel.add(role.getLabel()); } return rolesLabel; } @Override public List findResourcesList(String cacheKeyResources,String userId) { List resourcesList = new ArrayList(); if (simpleCacheService.getCache(cacheKeyResources) != null) { resourcesList = (List) simpleCacheService.getCache(cacheKeyResources).get(cacheKeyResources); } else { resourcesList = userAdapterFace.findResourceByUserId(userId); if (resourcesList.size() > 0) { //用户资源存放到map Map mapResource = new HashMap(); mapResource.put(cacheKeyResources, resourcesList); //新建SimpleMapCache实例并放入缓存管理器 SimpleMapCache cacheResource = new SimpleMapCache(cacheKeyResources, mapResource); simpleCacheService.creatCache(cacheKeyResources, cacheResource); } } List resourcesLabel = new ArrayList(); for (ResourceVo resources : resourcesList) { resourcesLabel.add(resources.getLabel()); } return resourcesLabel; } @Override public UserVo findUserByLoginName(String loginName) { String key = CacheConstant.FIND_USER_BY_LOGINNAME+loginName; RBucket rBucket = redissonClient.getBucket(key); UserVo user = rBucket.get(); if (!EmptyUtil.isNullOrEmpty(user)) { return user; }else { user = userAdapterFace.findUserByLoginName(loginName); if (!EmptyUtil.isNullOrEmpty(user)) { rBucket.set(user, 300, TimeUnit.SECONDS); return user; } } rBucket.set(new UserVo(), 3, TimeUnit.SECONDS); return null; } @Override public List findResourcesIdsList(String cacheKeyResources,String userId) { List resourcesList = new ArrayList(); if (simpleCacheService.getCache(cacheKeyResources) != null) { resourcesList = (List) simpleCacheService.getCache(cacheKeyResources).get(cacheKeyResources); } else { resourcesList = userAdapterFace.findResourceByUserId(userId); if (resourcesList.size() > 0) { //用户资源存放到map Map mapResource = new HashMap(); mapResource.put(cacheKeyResources, resourcesList); //新建SimpleMapCache实例并放入缓存管理器 SimpleMapCache cacheResource = new SimpleMapCache(cacheKeyResources, mapResource); simpleCacheService.creatCache(cacheKeyResources, cacheResource); } } List resourcesLabel = new ArrayList(); for (ResourceVo resources : resourcesList) { resourcesLabel.add(resources.getId()); } return resourcesLabel; } @Override public void loadUserAuthorityToCache(ShiroUser user) { String sessionId = user.getSessionId(); List roles = userAdapterFace.findRoleByUserId(user.getId()); //创建角色cachaeKey String cacheKeyRole = CacheConstant.ROLE_KEY + sessionId; //用户角色存放到map Map mapRole = new HashMap(); mapRole.put(cacheKeyRole, roles); //新建SimpleMapCache实例并放入缓存管理器 SimpleMapCache cacheRole = new SimpleMapCache(cacheKeyRole, mapRole); simpleCacheService.creatCache(cacheKeyRole, cacheRole); List resourcesList = userAdapterFace.findResourceByUserId(user.getId()); if (resourcesList.size() > 0) { //创建资源cachaeKey String cacheKeyResources = CacheConstant.RESOURCES_KEY + sessionId; //用户资源存放到map Map mapResource = new HashMap(); mapResource.put(cacheKeyResources, resourcesList); //新建SimpleMapCache实例并放入缓存管理器 SimpleMapCache cacheResource = new SimpleMapCache(cacheKeyResources, mapResource); simpleCacheService.creatCache(cacheKeyResources, cacheResource); } }}
复制代码 shiro-gateway网关
原理分析
1、依赖springboot-shiro-framework-client实现认证、鉴权、过滤器定义、会话统一、realm缓存等功能
2、springboot-shiro-mgt管理后台持久化网关资源
3、springboot-shiro-handler实现网关资源查询服务化
4、gateway-service依据持久化的网关资源,动态创建消费端服务
代码实现
网关资源持久化
这里在原有资源的基础上,增加的网关资源的管理:
1、定义网关systemcode,用以区分不同网关系统
2、定义访问的路径
3、定义资源的唯一标识,作为权限控制的标识
4、定义业务端dubbo服务端接口、目标方法、传入阐述、轮训算法、超时时间、重试次数等参数,这些内容会在gateway-service项目中解析
网关资源服务化
- ResourceAdapterFace:网关资源服务接口ResourceAdapterFaceImpl:网关资源服务接口实现ResourceBridgeService:网关资源桥接器接口ResourceBridgeServiceImpl:网关资源桥接器接口实现
复制代码- package com.itheima.shiro.face;import com.itheima.shiro.vo.ResourceVo;import java.util.List;/** * @Description:网关资源服务接口 */public interface ResourceAdapterFace { /** * @Description 获得当前系统是由有效的dubbo的资源 */ List findValidResourceVoAll(String systemCode);}
复制代码- package com.itheima.shiro.faceImpl;import com.itheima.shiro.face.ResourceAdapterFace;import com.itheima.shiro.pojo.Resource;import com.itheima.shiro.service.ResourceService;import com.itheima.shiro.utils.BeanConv;import com.itheima.shiro.utils.EmptyUtil;import com.itheima.shiro.vo.ResourceVo;import org.apache.dubbo.config.annotation.Service;import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/** * @Description:网关资源服务接口实现 */@Service(version = "1.0.0", retries = 3,timeout = 5000)public class ResourceAdapterFaceImpl implements ResourceAdapterFace { // 网关资源的持久化服务 @Autowired ResourceService resourceService; @Override public List findValidResourceVoAll(String systemCode) { List resourceList = resourceService.findValidResourceVoAll(systemCode); if (!EmptyUtil.isNullOrEmpty(resourceList)){ return BeanConv.toBeanList(resourceList, ResourceVo.class); } return null; }}
复制代码- package com.itheima.shiro.core.bridge;import com.itheima.shiro.vo.ResourceVo;import java.util.List;/** * @Description:网关资源桥接器接口 */public interface ResourceBridgeService { /** * @Description 查询当前系统所有有效的DUBBO类型的服务 * @param systemCode 系统编号:与mgt添加系统编号相同 * @return */ public List findValidResourceVoAll(String systemCode);}
复制代码- package com.itheima.shiro.client;import com.itheima.shiro.core.bridge.ResourceBridgeService;import com.itheima.shiro.face.ResourceAdapterFace;import com.itheima.shiro.vo.ResourceVo;import org.apache.dubbo.config.annotation.Reference;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.util.List;/** * @Description:网关资源桥接器接口实现 */@Component("resourceBridgeService")public class ResourceBridgeServiceImpl implements ResourceBridgeService { @Value("${itheima.resource.systemcode}") private String systemCode; @Reference(version = "1.0.0") ResourceAdapterFace resourceAdapterFace; @Override public List findValidResourceVoAll(String systemCode) { return resourceAdapterFace.findValidResourceVoAll(systemCode); }}
复制代码 动态消费端
CacheWare:缓存仓库
CacheWareService:缓存仓库服务接口
CacheWareServiceImpl:缓存仓库服务接口实现
CacheWareSyncService:缓存仓库同步服务接口
CacheWareSyncServiceImpl:缓存仓库同步服务接口实现
LoginAction:登录相应接口
GateWayController:相应层的统一入口- package com.itheima.shiro.pojo;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import java.lang.reflect.Method;import java.lang.reflect.Type;/** * @Description 缓存仓库可执行类 */@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic class CacheWare { //执行类 private String serviceName; //执行方法 private String methodName; //方法对象 private Method method; //参数类型 private Class methodParamsClass; //代理类 private Object proxy;}
复制代码 CacheWareService
其主要负责:
1、缓存的清除
2、向map容器中创建缓存
3、获得缓存仓库执行对象- package com.itheima.shiro.cache;import com.google.common.collect.Multimap;import com.itheima.shiro.pojo.CacheWare;/** * @Description:缓存仓库服务 */public interface CacheWareService { /** * @Description 清除缓存 */ void clearCacheWare(); /** * @Description 向map容器中创建缓存 * @param cacheWareMap */ void createCacheWare(Multimap cacheWareMap); /** * @Description 获得缓存仓库执行对象 * @param serviceName 服务名 * @param methodName 方法名 * @return {@link CacheWare} * */ CacheWare queryCacheWare(String serviceName, String methodName);}
复制代码- package com.itheima.shiro.cache.impl;import com.google.common.collect.ArrayListMultimap;import com.google.common.collect.Multimap;import com.itheima.shiro.cache.CacheWareService;import com.itheima.shiro.pojo.CacheWare;import com.itheima.shiro.utils.EmptyUtil;import org.springframework.stereotype.Service;import java.util.Collection;/** * @Description:缓存仓库服务实现 */@Service("cacheWareService")public class CacheWareServiceImpl implements CacheWareService { private Multimap cacheWareMap = ArrayListMultimap.create(); @Override public void clearCacheWare() { cacheWareMap.clear(); } @Override public void createCacheWare(Multimap cacheWareMap) { this.cacheWareMap = cacheWareMap; } @Override public CacheWare queryCacheWare(String serviceName, String methodName) { if (EmptyUtil.isNullOrEmpty(serviceName)||EmptyUtil.isNullOrEmpty(methodName)){ return null; } String key = serviceName+":"+methodName; Collection cacheWares =cacheWareMap.get(key); return EmptyUtil.isNullOrEmpty(cacheWares)?null:cacheWares.iterator().next(); }}
复制代码 CacheWareSyncService
其主要职责:
1、启动时、调用CacheWareService的创建缓存方法初始化缓存仓库
2、同步缓存仓库
3、网关资源转化缓存仓库可执行对象
4、从dubbo中,初始化代理对象
注意:为了在多个网关系统下,接口转换的无干扰,读取的只是本网关所对应的资源- package com.itheima.shiro.cache;import com.itheima.shiro.pojo.CacheWare;import com.itheima.shiro.vo.ResourceVo;/** * @Description:缓存仓库同步接口 */public interface CacheWareSyncService { /** * @Description 初始化缓存仓库 */ void initCacheWare(); /** * @Description 同步缓存仓库 */ void refreshCacheWare(); /** * @Description 资源转换缓存仓库可执行对象 */ CacheWare resourceConvCacheWare(ResourceVo resource); /** * @Description 初始化代理对象 * @param interfaceClass 接口 * @param loadbalance 算法 * @param version 版本 * @param timeout 超时时间 * @param retries 重试次数 */ Object initProxy(Class interfaceClass, String loadbalance, String version, Integer timeout, Integer retries); /** * @Description 回收资源 */ void destoryCacheWare();}
复制代码- package com.itheima.shiro.cache.impl;import com.google.common.collect.ArrayListMultimap;import com.google.common.collect.Multimap;import com.itheima.shiro.cache.CacheWareService;import com.itheima.shiro.cache.CacheWareSyncService;import com.itheima.shiro.core.bridge.ResourceBridgeService;import com.itheima.shiro.pojo.CacheWare;import com.itheima.shiro.utils.EmptyUtil;import com.itheima.shiro.vo.ResourceVo;import lombok.extern.log4j.Log4j2;import org.apache.dubbo.config.ApplicationConfig;import org.apache.dubbo.config.ReferenceConfig;import org.apache.dubbo.config.RegistryConfig;import org.apache.dubbo.config.utils.ReferenceConfigCache;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;import javax.annotation.Resource;import java.lang.reflect.Method;import java.util.List;import java.util.Set;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * @Description:缓存仓库同步服务接口实现 */@Service("cacheWareSyncService")@Log4j2public class CacheWareSyncServiceImpl implements CacheWareSyncService { @Value("${itheima.resource.systemcode}") private String systemCode; @Autowired CacheWareService cacheWareService; @Autowired ResourceBridgeService resourceBridgeService; @Autowired private ApplicationConfig applicationConfig; @Autowired private RegistryConfig registryConfig; //线程池 private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); @Override @PostConstruct public void initCacheWare() { executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { refreshCacheWare(); } }, 0, 2, TimeUnit.MINUTES); } @Override public void refreshCacheWare() { //获得网关资源 List resources = resourceBridgeService.findValidResourceVoAll(systemCode); //判断网关资源是否为空,则清空所有map容器中的可执行对象 if (EmptyUtil.isNullOrEmpty(resources)){ cacheWareService.clearCacheWare(); } Multimap cacheWareMultimap = ArrayListMultimap.create(); //把网关资源转换为缓存仓库可执行对象 for (ResourceVo resource : resources) { if (EmptyUtil.isNullOrEmpty(resource.getServiceName())|| EmptyUtil.isNullOrEmpty(resource.getMethodName())){ log.warn("网关资源定义不完整:{}",resource.toString()); continue; } CacheWare cacheWare = resourceConvCacheWare(resource); if (!EmptyUtil.isNullOrEmpty(cacheWare)){ String key = cacheWare.getServiceName()+":"+cacheWare.getMethodName(); cacheWareMultimap.put(key, cacheWare); } } //放入map容器 cacheWareService.createCacheWare(cacheWareMultimap); } @Override public CacheWare resourceConvCacheWare(ResourceVo resource) { //网关资源服务接口 Class serviceClass = null; try { serviceClass = Class.forName(resource.getServiceName()); } catch (ClassNotFoundException e) { log.error("为在容器中发现{}接口类",resource.getServiceName()); return null; } String serviceNameAll = resource.getServiceName(); // 获得类名 String serviceName = serviceNameAll.substring(serviceNameAll.lastIndexOf(".")+1).toLowerCase(); //对应的执行方法 Method[] methods = serviceClass.getDeclaredMethods(); Method methodTarget = null; for (Method method : methods) { if (method.getName().equals(resource.getMethodName())){ methodTarget = method; break; } } //如果方法获取失败 if (EmptyUtil.isNullOrEmpty(methodTarget)){ log.error("为在容器中发现{}方法",resource.getMethodName()); return null; } //获得方法上的传入参数 Class[] parameterTypes = methodTarget.getParameterTypes(); Class methodParamsClassTarget = null; for (Class parameterType : parameterTypes) { if (parameterType.getName().equals(resource.getMethodParam())){ methodParamsClassTarget = parameterType; break; } } //构建服务代理类 Object proxy = initProxy(serviceClass, resource.getLoadbalance(), resource.getDubboVersion(), resource.getTimeout(), resource.getRetries()); //构建缓存仓库可执行对象 CacheWare cacheWare = CacheWare.builder() .serviceName(serviceName) .methodName(resource.getMethodName()) .method(methodTarget) .methodParamsClass(methodParamsClassTarget) .proxy(proxy).build(); return cacheWare; } // 获取代理类 @Override public Object initProxy(Class interfaceClass, String loadbalance, String version, Integer timeout, Integer retries) { // 构建消费端 ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setApplication(applicationConfig); referenceConfig.setRegistry(registryConfig); referenceConfig.setLoadbalance(EmptyUtil.isNullOrEmpty(loadbalance)?"random":loadbalance); referenceConfig.setInterface(interfaceClass); referenceConfig.setVersion(version); referenceConfig.setTimeout(EmptyUtil.isNullOrEmpty(timeout)?20000:timeout); referenceConfig.setCheck(false); // 指定重试次数 referenceConfig.setRetries(EmptyUtil.isNullOrEmpty(retries)?0:retries); ReferenceConfigCache cache = ReferenceConfigCache.getCache(); return cache.get(referenceConfig); } @Override public void destoryCacheWare() { executorService.shutdownNow(); }}
复制代码 网关资源解析
其主要负责:
1、传入参数处理
2、获得可执行缓存仓库
3、执行远程服务
4、处理返回结果- package com.itheima.shiro.web;import com.alibaba.fastjson.JSONObject;import com.itheima.shiro.base.BaseRequest;import com.itheima.shiro.cache.CacheWareService;import com.itheima.shiro.constant.GateWayConstant;import com.itheima.shiro.pojo.CacheWare;import com.itheima.shiro.response.MultiResponse;import com.itheima.shiro.response.PageResponse;import com.itheima.shiro.response.SingleResponse;import com.itheima.shiro.utils.EmptyUtil;import com.itheima.shiro.view.JsonResult;import lombok.extern.log4j.Log4j2;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.lang.reflect.Method;/** * @Description:网关统一入口 */@Controller@Log4j2public class GateWayController { @Autowired CacheWareService cacheWareService; /** * @Description 请求入口 * @param serviceName 服务名称 * @param methodName 目标方法 * @param baseRequest 请求对象 * @return */ @RequestMapping(value = "{serviceName}/{methodName}",method = RequestMethod.POST) @ResponseBody public JsonResult postGateWay(@PathVariable("serviceName") String serviceName, @PathVariable("methodName") String methodName, @RequestBody BaseRequest baseRequest) throws Exception{ JsonResult jsonResult = null; if (EmptyUtil.isNullOrEmpty(serviceName)||EmptyUtil.isNullOrEmpty(methodName)){ jsonResult = jsonResult.builder() .result(GateWayConstant.FAIL) .msg("参数缺失") .code(GateWayConstant.PARAMETERS_MISSING) .build(); return jsonResult; } //传入参数处理 JSONObject jsonObject = null; Object datas = baseRequest.getDatas(); if (!EmptyUtil.isNullOrEmpty(datas)){ jsonObject = JSONObject.parseObject(jsonObject.toJSONString(datas)); } //获得缓存仓库可执行对象 CacheWare cacheWare = cacheWareService.queryCacheWare(serviceName, methodName); //执行远程服务 Object proxy = cacheWare.getProxy(); Method method = cacheWare.getMethod(); Class methodParamsClass = cacheWare.getMethodParamsClass(); Object result = null; if (EmptyUtil.isNullOrEmpty(methodParamsClass)){ result = method.invoke(proxy); }else { Object arg = JSONObject.toJavaObject(jsonObject, methodParamsClass); result = method.invoke(proxy,arg); } //处理放回结果 return convResult(result); } /** * @Description 处理请求结果 */ private JsonResult convResult(Object result) { JsonResult jsonResult = JsonResult.builder() .result(GateWayConstant.SUCCEED) .msg("相应正常") .code(GateWayConstant.SUCCEED_CODE) .build(); if (EmptyUtil.isNullOrEmpty(result)) { jsonResult = JsonResult.builder() .result(GateWayConstant.FAIL) .msg("返回结果为空") .code(GateWayConstant.RESULT_ISNULLOREMPTY) .build(); return jsonResult; } if (result instanceof SingleResponse) { BeanUtils.copyProperties(result, jsonResult); @SuppressWarnings("rawtypes") SingleResponse singleResponse = (SingleResponse) result; jsonResult.setDatas(singleResponse.getValue()); } else if (result instanceof MultiResponse) { BeanUtils.copyProperties(result, jsonResult); @SuppressWarnings("rawtypes") MultiResponse multiResponse = (MultiResponse) result; jsonResult.setDatas(multiResponse.getValues()); } else if (result instanceof PageResponse) { BeanUtils.copyProperties(result, jsonResult); PageResponse pageResponse = (PageResponse)result; jsonResult.setDatas( pageResponse.getValues()); } else { jsonResult = JsonResult.builder() .result(GateWayConstant.FAIL) .msg("返回结果格式不正确") .code(GateWayConstant.RESULT_MISSING) .build(); return jsonResult; } return jsonResult; }}
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |