SpringCloud-OAuth2(四):改造篇
本片主要讲SpringCloud Oauth2篇的实战改造,如动态权限、集成JWT、更改默认url、数据库加载client信息等改造。
同时,这应该也是我这系列博客的完结篇。
关于Oauth2,我也想说几句:
如果真的要应用到企业级项目当中去,必须要进行充足的准备,因为默认的配置、UI等很多都不是通用的(不适用于各个公司),
但是这套框架还好留了很多适配方法等,因此其需要修改的配置、处理器、方法重写等逻辑确实很多很多。
话不多说,正文开始。
承接前文:
SpringCloud-OAuth2(一):基础篇
SpringCloud-OAuth2(二):实战篇
SpringCloud-OAuth2(三):进阶篇
1:动态权限
常用的权限校验机制如以下几点:
参考:https://zhuanlan.zhihu.com/p/144580287
| 类型 | 示例 |
|---|---|
| 硬编码 | 如在接口上添加注解:@PreAuthorize("hasAnyRole('ROLE_ADMIN')") |
| HttpSecurity动态增加 | 在资源服务配置类中配置,如:.authorizeRequests().anyRequest().authenticated() |
如果出现用户所拥有的权限出现变化时,上述两种是无法满足的。
百度了几天后网上确实有同学给出了不错的示例,其工作机制如下:


1.1:AccessDecisionManager重写
@Component
public class VipAccessDecisionManager implements AccessDecisionManager {
/**
* 决定当前用户是否有权限访问该请求
**/
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute configAttribute : configAttributes) {
//将访问所需资源或用户拥有资源进行比对
String needAuthority = configAttribute.getAttribute();
if (needAuthority == null) {
continue;
}
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("抱歉,您没有访问权限");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
1.2:FilterInvocationSecurityMetadataSource重写
public class VipSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
private Set<PermRoleEntity> permRoleEntitySet;
private final FilterInvocationSecurityMetadataSource superMetadataSource;
private final VipSecurityOauthService vipSecurityOauthService;
public VipSecurityMetadataSource(FilterInvocationSecurityMetadataSource superMetadataSource,
VipSecurityOauthService vipSecurityOauthService) {
this.superMetadataSource = superMetadataSource;
this.vipSecurityOauthService = vipSecurityOauthService;
}
private void loadPerms() {
permRoleEntitySet = vipSecurityOauthService.loadPerms();
}
/**
* 返回能访问该请求的所有角色集合
**/
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
loadPerms();
FilterInvocation fi = (FilterInvocation) object;
String access_uri = fi.getRequestUrl();
for (PermRoleEntity permRoleEntity : permRoleEntitySet) {
if (ANT_PATH_MATCHER.match(permRoleEntity.getAccessUri(), access_uri))
return permRoleEntity.getConfigAttributeList();
}
return superMetadataSource.getAttributes(object);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
loadPerms();
Set<ConfigAttribute> attributeSet = new HashSet<>();
permRoleEntitySet.stream().map(PermRoleEntity::getConfigAttributeList).forEach(attributeSet::addAll);
return attributeSet;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
1.3:动态权限入口(自定义)
VipSecurityOauthService:权限的动态加载api
这里写成静态的,好参考。
@Component
public class VipSecurityOauthService {
/**
* 动态加载权限-角色信息
**/
public Set<PermRoleEntity> loadPerms() {
Set<PermRoleEntity> permRoleEntitySet = new HashSet<>();
permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/demo/admin").setConfigAttributeList(SecurityConfig.createList("admin")));
permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/auth/**").setConfigAttributeList(SecurityConfig.createList("admin")));
permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/demo/sp-admin").setConfigAttributeList(SecurityConfig.createList("sp_admin")));
return permRoleEntitySet;
}
}
PermRoleEntity:url和角色对应关系
@Data
@Accessors(chain = true)
public class PermRoleEntity {
/**
* 访问的接口
**/
private String accessUri;
/**
* 可访问该接口的角色集合
**/
private List<ConfigAttribute> configAttributeList;
}
1.4:ResourceServer进行配置

1.5:测试
获取用户角色为admin的token,进行接口访问
①:用户admin角色的用户访问admin管理的接口

①:用户admin角色的用户访问sp-admin管理的接口

2:集成JWT
2.1:需要配置的bean
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
@Primary
public AuthorizationServerTokenServices defaultTokenServices(TokenStore tokenStore,JwtAccessTokenConverter jwtAccessTokenConverter) {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(tokenStore);
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter);
defaultTokenServices.setAccessTokenValiditySeconds(3600);
defaultTokenServices.setAccessTokenValiditySeconds(7200);
return defaultTokenServices;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
// 签名密钥
jwtAccessTokenConverter.setSigningKey("sign_key");
// 验证密钥
jwtAccessTokenConverter.setVerifier(new MacSigner("sign_key"));
return jwtAccessTokenConverter;
}
2.2:认证服务配置更改

tokenservice: 创建token、刷新token的地方。
获取token

2.3:OAuth2 Client
在前文中提到 OAuth2 Client Service 处理请求的时候是无法识别token的,需要远程提交给认证中心(OAuth2 Server)去识别token。
token换成JWT后客户端服务(OAuth2 Client Service)也可以识别token了,均需做如下配置:
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
// 签名密钥
jwtAccessTokenConverter.setSigningKey("sign_key");
// 验证密钥
jwtAccessTokenConverter.setVerifier(new MacSigner("sign_key"));
return jwtAccessTokenConverter;
}
前文中提到的这项配置也可以丢弃了。

访问客户端服务的接口测试:

3:更改默认url
3.1:框架提供的URL路径:
| 默认url | 作用 |
|---|---|
| /oauth/authorize | 授权端点 |
| /oauth/token | 令牌端点 |
| /oauth/confirm_access | 用户批准授权的端点 |
| /oauth/error | 用于渲染授权服务器的错误 |
| /oauth/check_token | 资源服务器解码access token |
| /oauth/check_token | 当使用JWT的时候,暴露公钥的端点 |
3.2:如何更改
可以按照下面这张方法进行重新转交接口地址:

自定义处理接口代码:
@RestController
public class AuthController extends WebApiController {
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
private final ConsumerTokenServices consumerTokenServices;
private final TokenEndpoint tokenEndpoint;
private final AuthorizationEndpoint authorizationRequest;
private final WhitelabelApprovalEndpoint whitelabelApprovalEndpoint;
private final WhitelabelErrorEndpoint whitelabelErrorEndpoint;
public AuthController(ConsumerTokenServices consumerTokenServices, TokenEndpoint tokenEndpoint,
AuthorizationEndpoint authorizationRequest, WhitelabelApprovalEndpoint whitelabelApprovalEndpoint,
WhitelabelErrorEndpoint whitelabelErrorEndpoint) {
this.consumerTokenServices = consumerTokenServices;
this.tokenEndpoint = tokenEndpoint;
this.authorizationRequest = authorizationRequest;
this.whitelabelApprovalEndpoint = whitelabelApprovalEndpoint;
this.whitelabelErrorEndpoint = whitelabelErrorEndpoint;
}
/**
* 自定义登录接口
*/
@PostMapping(value = "/login")
public ResponseEntity<String> login(@RequestBody UserLoginDto userLoginDto, Principal principal) throws HttpRequestMethodNotSupportedException {
Map<String, String> mapDto = ObjectMapperUtil.str2Obj(userLoginDto, new TypeReference<Map<String, String>>() {
});
mapDto.put(GrantTypeConstants.GRANT_TYPE, GrantTypeConstants.PASSWORD);
OAuth2AccessToken token;
try {
token = tokenEndpoint.postAccessToken(principal, mapDto).getBody();
if (token == null) {
throw new ServiceException("登录异常");
}
} catch (Exception e) {
if (e instanceof InvalidGrantException) {
throw new ServiceException("用户名密码错误");
} else {
throw e;
}
}
return response(WebApiResponse.ok(AuthToken.build(token)));
}
}
3.3:测试
4:数据库加载client信息
前文中以ProcessOn举例的QQ第三方登录就提到ProcessOn会向QQ申请一个client_id(客户端凭证),那么QQ第三方登录配置申请的入口必须将数据存放在数据库中,这样才能做到动态的新增、删除等,代码配置写死是不可能的。
注意:需要引入数据源、mysql驱动的依赖,并配置好数据源。
4.1:schema.sql
因为官方给的sql是hql的,我用的mysql8,因此做了一些类型上的修改。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for clientdetails
-- ----------------------------
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails` (
`appId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`resourceIds` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`appSecret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`grantTypes` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`redirectUrl` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`access_token_validity` int(0) NULL DEFAULT NULL,
`refresh_token_validity` int(0) NULL DEFAULT NULL,
`additionalInformation` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`autoApproveScopes` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`appId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of clientdetails
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`token` binary(1) NULL DEFAULT NULL,
`authentication_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`user_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authentication` binary(1) NULL DEFAULT NULL,
`refresh_token` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
`userId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`clientId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`expiresAt` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
`lastModifiedAt` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_approvals
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`resource_ids` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`client_secret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`access_token_validity` int(0) NULL DEFAULT NULL,
`refresh_token_validity` int(0) NULL DEFAULT NULL,
`additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('admin', 'admin', 'tgMj02VG9dkpeKUN5lSWSsKotIt2yTIElkFcvsqLnwGOspNYhe+Teg==', 'test,all', 'authorization_code,client_credentials,password,implicit,refresh_token', 'http://www.baidu.com', 'admin', NULL, NULL, NULL, 'all');
-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
`token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`token` binary(1) NULL DEFAULT NULL,
`authentication_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`user_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_client_token
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`authentication` binary(1) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_code
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`token` binary(1) NULL DEFAULT NULL,
`authentication` binary(1) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_refresh_token
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
在这个表中配置数据即可,参考代码静态配置:


4.2:配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
做好以上配置,认证服务就可以从数据库加载客户端凭证信息了。
本文参考博客:https://www.cnblogs.com/cjsblog/p/9184173.html
SpringCloud-OAuth2(四):改造篇的更多相关文章
- SpringCloud(四)- Hystris简介及@EnableCircuitBreaker 和 @HystrixCommand 注解的使用
唯能极于情,故能极于剑有问题或错误请及时联系小编或关注小编公众号 “CodeCow”,小编一定及时回复和改正,期待和大家一起学习交流 此文由四部分组成(Hystris简介.@EnableCircuit ...
- 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign)
转载请标明出处: https://www.fangzhipeng.com/springcloud/2017/07/12/sc03-feign/ 本文出自方志朋的博客 最新Finchley版本请访问: ...
- springcloud(十四):搭建Zuul微服务网关
springcloud(十四):搭建Zuul微服务网关 1. 2. 3. 4.
- 一个项目的SpringCloud微服务改造过程
SSO是公司一个已经存在了若干年的项目,后端采用SpringMVC.MyBatis,数据库使用MySQL,前端展示使用Freemark.今年,我们对该项目进行了一次革命性的改进,改造成SpringCl ...
- 跟我学SpringCloud | 第四篇:熔断器Hystrix
跟我学SpringCloud | 第四篇:熔断器Hystrix 1. 熔断器 服务雪崩 在正常的微服务架构体系下,一个业务很少有只需要调用一个服务就可以返回数据的情况,这种比较常见的是出现在demo中 ...
- 基于springcloud搭建项目-Feign篇(四)
上一篇已经写过ribbon客户端负载均衡的用法了,这篇主要是介绍feign的用法,首先我们必须了解feign是什么?能干嘛?怎么用? 这里简单介绍一下,然后进行代码测试 1.概述 Feign是一个声明 ...
- 史上最简单的SpringCloud教程 | 第十篇: 高可用的服务注册中心(Finchley版本)
转载请标明出处: 原文首发于 https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f10-eureka/ 本文出自方志朋的博客 文章 史上最简单 ...
- 史上最简单的SpringCloud教程 | 第十一篇: docker部署spring cloud项目
转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springcloud/2017/07/12/sc11-docker/ 本文出自方志朋的博客 一.docker简介 ...
- SpringCloud教程 | 第五篇: 路由网关(zuul)(Finchley版本)
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统.一个简答的微服务系统如下图: ...
随机推荐
- Python正则表达式的七个使用范例
本文由 伯乐在线 - 左手的灵魂 翻译.未经许可,禁止转载!英文出处:thegeekstuff.欢迎加入翻译组.http://blog.jobbole.com/74844/ 作为一个概念而言,正则表达 ...
- Redis 分布式锁|从青铜到钻石的五种演进方案
缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):Redis 分布式锁|从青铜到钻石的五种演进方案 缓存实战(三):分布式锁中的王者方案 - Redisson 上 ...
- 【转】java-selenium三种等待方式
方式1: 线程等待:Thread.sleep(xxxx) 只要在case中加入sleep就会强制等待设置的时间后才会执行之后的命令,这种等待一般适用于调试脚本的时候. java代码: //等待3秒 T ...
- 『动善时』JMeter基础 — 21、HTTP Cookie管理器的使用
目录 1.在HTTP信息头管理器组件中添加Cookie信息 (1)测试计划内包含的元件 (2)请求取样器内容 (3)HTTP信息头管理器内容 (4)查看结果 2.使用HTTP Cookie管理器组件来 ...
- [Qt] 《开发指南》samp4.1 源码分析
界面: 功能: 输入单价和数量,计算总价:进制转换 控件: Qlabel QLineEdit QPushButton 文件依赖关系图(depend on): main.cpp:程序入口 widget. ...
- 。 (有些情况下通过 lsof(8) 或 fuser(1) 可以 找到有关使用该设备的进程的有用信息)
umount时目标忙解决办法 标签(空格分隔): ceph ceph运维 osd 在删除osd后umount时,始终无法umonut,可以通过fuser查看设备被哪个进程占用,之后杀死进程,就可以顺利 ...
- linux(centos 7)下安装JDK,Tomcat,mysql 运行Maven 项目
一.在Linux中安装JDK 1. 将JDK上传到root下(任何位置均可以). 如图: 2. 用解压命令解压JDK tar -xvf (此处为jdk文件名) 如果是rpm包,执行rpm -i jdk ...
- JQuery 基础之基本选择器
1.什么是jQuery选择器: jQuery选择器继承了CSS与Path语言的部分语法,允许通过标签名.属性名或内容对DOM元素进行快速.准确的选择,而不必担心浏览器的兼容性,通过jQuery选择器对 ...
- Idea项目上传到gitlab(以新建项目为例)
1.首先,需要你自己登录GitLab,并新建一个项目的链接,如下图所示: 图一: 图二: 图三(idea上传时用到此链接): 2.在idea上新建一个demo项目,创建一个Git仓库: 3.点击创建后 ...
- 改进遗传算法之CHC算法简要介绍
简要介绍: CHC算法是Eshelman于1991年提出的一种改进的遗传算法的缩称,第一个C代表跨世代精英选择(Cross generational elitist selection)策略, H代表 ...