SpringBoot-shiro-jwt-dubbo-redis分布式统一权限系统(完结)
源码下载地址:配套源码地址
设计

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
@AllArgsConstructor
public 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<FilterChainVo> 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<FilterChainVo> findFilterChainList() {
List<FilterChain> 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<FilterChainVo> 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<FilterChainVo> 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<FilterChainVo> 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<String, NamedFilterList> 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<FilterChainVo> 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<String, Filter>());
setFilterChains(new LinkedHashMap<String, NamedFilterList>());
addDefaultFilters(true);
}
@PostConstruct
public void init(){
Map<String, Filter> 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<String,Filter> customFilters){
for (Map.Entry<String, Filter> 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:自定义路径解析器
*/
@Log4j2
public 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过滤器工厂
*/
@Log4j2
public 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})
@Log4j2
public 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<String, Filter> filters() {
Map<String, Filter> map = new HashMap<String, Filter>();
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<FilterChainVo> 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<ResourceVo> 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<String> findRoleList(String cacheKeyRole, String userId) {
List<RoleVo> roles = new ArrayList<RoleVo>();
if (simpleCacheService.getCache(cacheKeyRole) != null) {
roles = (List<RoleVo>) simpleCacheService.getCache(cacheKeyRole).get(cacheKeyRole);
} else {
roles = userAdapterFace.findRoleByUserId(userId);
if (roles.size() > 0) {
//用户角色存放到map
Map<Object, Object> mapRole = new HashMap<Object, Object>();
mapRole.put(cacheKeyRole, roles);
//新建SimpleMapCache实例并放入缓存管理器
SimpleMapCache cacheRole = new SimpleMapCache(cacheKeyRole, mapRole);
simpleCacheService.creatCache(cacheKeyRole, cacheRole);
}
}
List<String> rolesLabel = new ArrayList<String>();
for (RoleVo role : roles) {
rolesLabel.add(role.getLabel());
}
return rolesLabel;
}
@Override
public List<String> findResourcesList(String cacheKeyResources,String userId) {
List<ResourceVo> resourcesList = new ArrayList<ResourceVo>();
if (simpleCacheService.getCache(cacheKeyResources) != null) {
resourcesList = (List<ResourceVo>) simpleCacheService.getCache(cacheKeyResources).get(cacheKeyResources);
} else {
resourcesList = userAdapterFace.findResourceByUserId(userId);
if (resourcesList.size() > 0) {
//用户资源存放到map
Map<Object, Object> mapResource = new HashMap<Object, Object>();
mapResource.put(cacheKeyResources, resourcesList);
//新建SimpleMapCache实例并放入缓存管理器
SimpleMapCache cacheResource = new SimpleMapCache(cacheKeyResources, mapResource);
simpleCacheService.creatCache(cacheKeyResources, cacheResource);
}
}
List<String> resourcesLabel = new ArrayList<String>();
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<UserVo> 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<String> findResourcesIdsList(String cacheKeyResources,String userId) {
List<ResourceVo> resourcesList = new ArrayList<ResourceVo>();
if (simpleCacheService.getCache(cacheKeyResources) != null) {
resourcesList = (List<ResourceVo>) simpleCacheService.getCache(cacheKeyResources).get(cacheKeyResources);
} else {
resourcesList = userAdapterFace.findResourceByUserId(userId);
if (resourcesList.size() > 0) {
//用户资源存放到map
Map<Object, Object> mapResource = new HashMap<Object, Object>();
mapResource.put(cacheKeyResources, resourcesList);
//新建SimpleMapCache实例并放入缓存管理器
SimpleMapCache cacheResource = new SimpleMapCache(cacheKeyResources, mapResource);
simpleCacheService.creatCache(cacheKeyResources, cacheResource);
}
}
List<String> resourcesLabel = new ArrayList<String>();
for (ResourceVo resources : resourcesList) {
resourcesLabel.add(resources.getId());
}
return resourcesLabel;
}
@Override
public void loadUserAuthorityToCache(ShiroUser user) {
String sessionId = user.getSessionId();
List<RoleVo> roles = userAdapterFace.findRoleByUserId(user.getId());
//创建角色cachaeKey
String cacheKeyRole = CacheConstant.ROLE_KEY + sessionId;
//用户角色存放到map
Map<Object, Object> mapRole = new HashMap<Object, Object>();
mapRole.put(cacheKeyRole, roles);
//新建SimpleMapCache实例并放入缓存管理器
SimpleMapCache cacheRole = new SimpleMapCache(cacheKeyRole, mapRole);
simpleCacheService.creatCache(cacheKeyRole, cacheRole);
List<ResourceVo> resourcesList = userAdapterFace.findResourceByUserId(user.getId());
if (resourcesList.size() > 0) {
//创建资源cachaeKey
String cacheKeyResources = CacheConstant.RESOURCES_KEY + sessionId;
//用户资源存放到map
Map<Object, Object> mapResource = new HashMap<Object, Object>();
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<ResourceVo> 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<ResourceVo> findValidResourceVoAll(String systemCode) {
List<Resource> 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<ResourceVo> 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<ResourceVo> 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
@NoArgsConstructor
public 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<String, CacheWare> 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<String, CacheWare> cacheWareMap = ArrayListMultimap.create();
@Override
public void clearCacheWare() {
cacheWareMap.clear();
}
@Override
public void createCacheWare(Multimap<String, CacheWare> 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<CacheWare> 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")
@Log4j2
public 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<ResourceVo> resources = resourceBridgeService.findValidResourceVoAll(systemCode);
//判断网关资源是否为空,则清空所有map容器中的可执行对象
if (EmptyUtil.isNullOrEmpty(resources)){
cacheWareService.clearCacheWare();
}
Multimap<String,CacheWare> 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<Object> 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
@Log4j2
public 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;
}
}
本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/19482539
浙公网安备 33010602011771号