修改配置文件,准备好四个域名

127.0.0.1  auth.server.com
127.0.0.1 user.server.com
127.0.0.1 third.server.com
127.0.0.1 eureka.server.com

注册中心:eureka-server服务

pom依赖

        <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>3.1.1</version>
</dependency>
<!--web项目驱动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-start-version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.0</version>
</dependency>

配置web项目启动类

/**
* @description: Eureka 服务端注册中心:剔除数据源操作
* @author: GuoTong
* @createTime: 2023-06-26 21:50
* @since JDK 1.8 OR 11
**/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaServer
public class EurekaApplication { public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}

配置跨域支持和静态资源过滤

/**
* @description: SpringBoot-Web配置
* @author: GuoTong
* @createTime: 2023-06-05 15:37
* @since JDK 1.8 OR 11
**/
@Configuration
public class SpringBootConfig implements WebMvcConfigurer { /**
* Description: 添加全局跨域CORS处理
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许证书
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
} /**
* Description: 静态资源过滤
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//ClassPath:/Static/** 静态资源释放
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
//释放swagger
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
//释放webjars
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
} }

配置文件基础配置

server:
port: 10086
spring:
application:
name: eureka-server
security:
user:
name: eureka
password: eureka
mvc:
static-path-pattern: classpath:/static/**
eureka:
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
register-with-eureka: false #自己不向注册中心注册自己
fetch-registry: false # 自己是注册中心
instance:
hostname: 127.0.0.1
prefer-ip-address: true

启动注册中心验证

准备Sa-Token认证中心server

https://sa-token.cc/doc.html#/ 可以自己参考官方网站定制

引入依赖

knife4j / mysql / mybatis / sa-token / commons / thymeleaf / loadbalancer / bootstrap /eureka-client / lombok

 <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> <!--Knife4j(增强Swagger)-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency> <!--Mysql数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-version}</version>
</dependency> <!--Mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.verison}</version>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatisplus.verison}</version>
</dependency> <!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.33.0</version>
</dependency> <!-- Sa-Token 插件:整合SSO -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>1.33.0</version>
</dependency> <!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.33.0</version>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency> <!-- 视图引擎(在前后端不分离模式下提供视图支持) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <!--新版的移除了Ribbon的负载策略,所需改用新版的loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>${spring-cloud-starter-version}</version>
</dependency> <!-- bootstrap最高级启动配置读取 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>${spring-cloud-starter-version}</version>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.1</version>
</dependency>

核心Sa-Token的接口


/**
* @description: 统一认证中心 SSO-Server用于对外开放接口:
* @author: GuoTong
* @createTime: 2022-11-26 23:03
* @since JDK 1.8 OR 11
**/
@RestController
public class SsoServerController { private AuthLoginUserService authLoginUserService; /**
* Description: 构造器注入
*/
public SsoServerController(AuthLoginUserService authLoginUserService) {
this.authLoginUserService = authLoginUserService;
} /*
* /*
* SSO-Server端:处理所有SSO相关请求
* http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd
* http://{host}:{port}/sso/checkTicket -- Ticket校验接口(isHttp=true时打开),接受参数:ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选]
* http://{host}:{port}/sso/signout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、secretkey=接口调用秘钥
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
return SaSsoProcessor.instance.serverDister();
} /**
* 配置SSO相关参数
*/
@Autowired
private void configSso(SaSsoConfig sso) {
// 配置:未登录时返回的View
sso.setNotLoginView(() -> new ModelAndView("sa-login.html")); // 配置:登录处理函数
sso.setDoLoginHandle((name, pwd) -> {
QueryWrapper<AuthLoginUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", name);
queryWrapper.eq("password", pwd);
AuthLoginUser user = authLoginUserService.getOne(queryWrapper);
if (user != null) {
StpUtil.login(user.getId());
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
}
return SaResult.error("登录失败!");
}); }
}

Http对外接口--简单几个示例

/**
* 前后台分离架构下集成SSO所需的代码 (SSO-Server端)
* <p>(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)</p>
*
* @author kong
*/
@RestController
public class H5Controller { @Autowired
private AuthLoginUserService authLoginUserService; /**
* 获取 redirectUrl
*/
@RequestMapping("/sso/getRedirectUrl")
private Object getRedirectUrl(String redirect, String mode) {
// 未登录情况下,返回 code=401
if (StpUtil.isLogin() == false) {
return SaResult.code(401);
}
// 已登录情况下,构建 redirectUrl
if (SaSsoConsts.MODE_SIMPLE.equals(mode)) {
// 模式一
SaSsoUtil.checkRedirectUrl(SaFoxUtil.decoderUrl(redirect));
return SaResult.data(redirect);
} else {
// 模式二或模式三
String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), redirect);
return SaResult.data(redirectUrl);
}
} @RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
return authLoginUserService.queryUserNameAndPassword(name, pwd);
} @RequestMapping(value = "isLogin", method = RequestMethod.GET)
public SaResult isLogin() {
return SaResult.ok("是否登录:" + StpUtil.isLogin());
} @RequestMapping(value = "tokenInfo", method = RequestMethod.GET)
public SaResult tokenInfo() {
return SaResult.data(StpUtil.getTokenInfo());
} @RequestMapping(value = "logout", method = RequestMethod.GET)
public SaResult logout() {
StpUtil.logout();
return SaResult.ok();
} }

Sa-Token的拦截器,权限

/**
* @description: 获取当前账号权限码集合
* @author: GuoTong
* @createTime: 2022-11-29 20:24
* @since JDK 1.8 OR 11
**/
@Component
@Slf4j
public class StpInterfaceImpl implements StpInterface { @Autowired
private AuthLoginUserService authLoginUserService; /**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
AuthLoginUser user = null;
try {
user = authLoginUserService.getById(loginId.toString());
} catch (Exception e) {
log.info("Id无效--->{}", loginId);
}
assert user != null;
String rule = user.getRule();
return Collections.singletonList(rule);
} /**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
AuthLoginUser user = null;
try {
user = authLoginUserService.getById(loginId.toString());
} catch (Exception e) {
log.info("Id无效--->{}", loginId);
}
assert user != null;
String rule = user.getRule();
return Collections.singletonList(rule);
} }

自定义拦截器,校验Sa-Token登录

/**
* @description: 授权认证:HandlerInterceptor需要注册拦截地址哦!!!
* @author: GuoTong
* @createTime: 2022-11-05 15:40
* @since JDK 1.8 OR 11
**/
@Component
public class AuthenticationInterceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 获取当前会话是否已经登录,返回true=已登录,false=未登录
String requestURI = httpServletRequest.getRequestURI();
// 判断是否是认证中心对外认证接口
if (requestURI.contains("/sso")) {
return true;
} // 如果不是映射到方法直接通过
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查是否有SkipTokenByJWT注释,有则跳过认证
if (method.isAnnotationPresent(SkipTokenByJWT.class)) {
SkipTokenByJWT SkipTokenByJWT = method.getAnnotation(SkipTokenByJWT.class);
if (SkipTokenByJWT.required()) {
return true;
}
}
// 否则需要登陆
if (!StpUtil.isLogin()) {
throw new NotLoginException(ContextCommonMsg.ERROR_MSG_4);
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(NeedTokenByJWT.class)) {
NeedTokenByJWT needTokenByJWT = method.getAnnotation(NeedTokenByJWT.class);
if (needTokenByJWT.required()) {
List<String> permissionList = StpUtil.getPermissionList();
// 查看当前用户是否包含当前接口的权限
if (!permissionList.contains(needTokenByJWT.rule())) {
throw new NotLoginException(ContextCommonMsg.ERROR_MSG_3);
}
}
}
return true;
} @Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception { } @Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
} }

全局异常捕获

/**
* @description: 全局异常处理:
* @author: GuoTong
* @createTime: 2022-11-27 11:17
* @since JDK 1.8 OR 11
**/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler { // 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
log.error("服务执行异常---->{}", e.getMessage());
return SaResult.error(e.getMessage());
} @ExceptionHandler(value = NotLoginException.class)
public SaResult handlerException(NotLoginException e) {
log.error("没有登陆---->{}", e.getMessage());
return SaResult.error(e.getMessage());
} @ExceptionHandler(value = SQLException.class)
public SaResult msgMySQLExecuteError(Exception e) {
e.printStackTrace();
log.error("Mysql执行异常");
String message = e.getMessage();
return SaResult.error(message);
} @ExceptionHandler(value = HttpMessageNotReadableException.class)
public SaResult msgNotFind(Exception e) {
e.printStackTrace();
log.error("请求错误");
String message = e.getMessage();
return SaResult.error("请求内容未传递" + message);
}
}

Springboot配置-跨域-注册拦截器-配置静态资源过滤-RestTemplate负载均衡

/**
* @description: SpringBoot-Web配置
* @author: GuoTong
* @createTime: 2023-06-05 15:37
* @since JDK 1.8 OR 11
**/
@Configuration
public class SpringBootConfig implements WebMvcConfigurer { /**
* Description: 增加拦截器
*
* @param registry
* @author: GuoTong
* @date: 2022-11-30 13:56:44
* @return:void
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/authLoginUser/**");
} @Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
} /**
* Description: 添加全局跨域CORS处理
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许证书
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
} /**
* Description: 静态资源过滤
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//ClassPath:/Static/** 静态资源释放
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
//释放swagger
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
//释放webjars
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

。。。。其余的东西详见项目。。。。。

https://gitee.com/gtnotgod/sa-token-sso-system.git

演示

认证中心登录页地址

接口管理认证中心用户

A系统USER-SERVICE

依赖

  <!--lombok-实体类简化依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> <!-- Sa-Token 权限认证, 在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${Sa-Token-version}</version>
</dependency>
<!-- Sa-Token 插件:整合SSO -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>${Sa-Token-version}</version>
</dependency> <!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${Sa-Token-version}</version>
</dependency> <!-- Sa-Token插件:权限缓存与业务缓存分离 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
<version>${Sa-Token-version}</version>
</dependency> <!--Open feign 服务间通讯HTTP-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-starter-version}</version>
</dependency> <!--Http内置的JDKHttpURLConnection替换为OkHttp-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>${feign-okhttp-version}</version>
</dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastJson-version}</version>
</dependency>
<!--web项目驱动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-start-version}</version>
</dependency>
<!--Knife4j(增强Swagger)-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency> <!-- bootstrap最高级启动配置读取 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>${spring-cloud-starter-version}</version>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.1</version>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

核心 SSO-Client 端认证接口


/**
* @description: 创建 SSO-Client 端认证接口
* @author: GuoTong
* @createTime: 2022-11-27 15:32
* @since JDK 1.8 OR 11
**/
@RestController
public class SSOClientController { /*
* SSO-Client端:处理所有SSO相关请求
* http://{host}:{port}/sso/login -- Client端登录地址,接受参数:back=登录后的跳转地址
* http://{host}:{port}/sso/logout -- Client端单点注销地址(isSlo=true时打开),接受参数:back=注销后的跳转地址
* http://{host}:{port}/sso/logoutCall -- Client端单点注销回调地址(isSlo=true时打开),此接口为框架回调,开发者无需关心
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
return SaSsoProcessor.instance.clientDister();
} // 首页
@RequestMapping("/")
public String index() {
String str = "<h2>Sa-Token SSO-Client</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">Login</a> " +
"<a href='/sso/logout?back=self'>Logout</a></p>";
return str;
}
}

拦截器和认证中心的AuthenticationInterceptor一致

核心全局异常处理

 @ExceptionHandler(value = NotLoginException.class)
public Resp handlerException(NotLoginException e,
HttpServletRequest request,
HttpServletResponse response) {
log.error("没有登陆---->{}", e.getMessage());
try {
response.sendRedirect("/sso/login?back=" + request.getRequestURL());
} catch (IOException ex) {
log.error("转到认证中心失败---->{}", ex.getMessage());
}
return Resp.error(e.getMessage());
}

核心配置

spring:
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 10000
# 连接池中的最大空闲连接
max-idle: 3
# 连接池中的最小空闲连接
min-idle: 0
jackson:
date-format: yyyy-MM-dd HH:mm:ss
mvc:
pathmatch:
matching-strategy: ant_path_matcher #Springboot2.6以上需要手动设置
static-path-pattern: classpath:/static/**
main:
allow-bean-definition-overriding: true # 重复定义bean的问题
logging:
level:
root: info
org.springframework: info # sa-token配置
sa-token:
# SSO-相关配置
sso:
# SSO-Server端 统一认证地址
auth-url: http://localhost:15001/sso/auth
# 是否打开单点注销接口
is-slo: true
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
alone-redis:
# Redis数据库索引
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
forest:
log-enabled: false # 关闭 forest 请求日志打印

。。。。其余的东西详见项目。。。。。

https://gitee.com/gtnotgod/sa-token-sso-system.git

演示

输入测试接口:http://user.server.com:13601/hello/getOne1/100011

被重定向到认证中心去了

登录访问后

同理准备好B服务THIRD-PARTY-SERVICE

选择注销A服务和B服务的其中一个;前提是没有登录多个账号

注销A服务 http://user.server.com:13601/sso/logout

注销B服务http://third.server.com:14302/sso/logout



A服务feign调用B服务

具体接口代码:

 /**
* 通过主键查询单条数据
*
* @param id 主键
* @return 单条数据
*/
@GetMapping("/queryOne/{id}")
public Resp<Object> selectOne(@PathVariable("id") String id) {
return Resp.Ok(this.openFeignRPCMySQlService.selectOne(id));
}

fegin接口

/**
* @description: OpenFegin调用third-party-service服务
* @FeignClient(name = "third-party-service") name指定调用的服务名
* @author: GuoTong
* @createTime: 2022-12-02 19:39
* @since JDK 1.8 OR 11
**/
@FeignClient(name = "third-party-service")
public interface OpenFeignRPCMySQlService { /**
* Description: feign调用third-party-service服务的接口
*
* @author: GuoTong
* @date: 2022-12-02 20:56:28
* @return:
*/
@GetMapping(value = "/hello/getOne2/{id}", produces = "application/json;charset=utf-8")
Resp<Object> selectOne(@PathVariable("id") String id); }

输入地址 http://user.server.com:13601/hello/queryOne/100011

重定向认证中心

登录A系统

fegin调用成功B系统

A系统已经登录,B系统未登录,访问B系统接口fegin调用A

不再重定向到认证中心,A已经登录,B已完成单点,校验已持有登录状态

注销A系统

http://user.server.com:13601/sso/logout

B系统再调用本系统的接口

已经重定向到认证中心

fegin调用A系统 :http://third.server.com:14302/hello/queryOne/100011

依旧重定向到认证中心

到此单点登录演示实现完毕;

个人项目地址,在gitee:https://gitee.com/gtnotgod/sa-token-sso-system.git

tip: 更多Sa-Token使用教程参考官方地址:https://sa-token.cc/doc.html#/

基于Sa-Token实现微服务之前的单点登录的更多相关文章

  1. 干货|基于 Spring Cloud 的微服务落地

    转自 微服务架构模式的核心在于如何识别服务的边界,设计出合理的微服务.但如果要将微服务架构运用到生产项目上,并且能够发挥该架构模式的重要作用,则需要微服务框架的支持. 在Java生态圈,目前使用较多的 ...

  2. 基于Spring Cloud的微服务落地

    微服务架构模式的核心在于如何识别服务的边界,设计出合理的微服务.但如果要将微服务架构运用到生产项目上,并且能够发挥该架构模式的重要作用,则需要微服务框架的支持. 在Java生态圈,目前使用较多的微服务 ...

  3. 基于Spring Cloud的微服务入门教程

    (本教程的原地址发布在本人的简书上:http://www.jianshu.com/p/947d57d042e7,若各位看官有什么问题或不同看法请在这里或简书留言,谢谢!) 本人也是前段时间才开始接触S ...

  4. Docker从入门到掉坑(二):基于Docker构建SpringBoot微服务

    本篇为Docker从入门到掉坑第二篇:基于Docker构建SpringBoot微服务,没有看过上一篇的最好读过 Docker 从入门到掉坑 之后,阅读本篇. 在之前的文章里面介绍了如何基于docker ...

  5. 基于Openshift的SpringBoot微服务

    基于Openshift的SpringBoot微服务 OpenShift是红帽的云开发平台即服务(PaaS).自由和开放源码的云计算平台使开发人员能够创建.测试和运行他们的应用程序,并且可以把它们部署到 ...

  6. 基于 Spring Cloud 的微服务架构实践指南(下)

    show me the code and talk to me,做的出来更要说的明白 本文源码,请点击learnSpringCloud 我是布尔bl,你的支持是我分享的动力! 一.引入 上回 基于 S ...

  7. 基于 Spring Cloud 的微服务脚手架

    基于 Spring Cloud 的微服务脚手架 作者: Grey 原文地址: 博客园:基于 Spring Cloud 的微服务脚手架 CSDN:基于 Spring Cloud 的微服务脚手架 本文主要 ...

  8. FineReport和泛微OA(Ecology)的单点登录集成方案

    最近出现了很多关于帆软报表和泛微OA的集成问题,均出现在“单点登录”上.直接也有相关的文章介绍一些FineReport和泛微集成的背景.价值等,以及FineReport和OA的深度集成的方案,但是并没 ...

  9. 《基于.NET Core构建微服务》系列文章(更新至第6篇,最新第7篇,已发布主页候选区)

    原文:Building Microservices On .NET Core – Part 1 The Plan 时间:2019年1月14日 作者:Wojciech Suwała, Head Arch ...

  10. 基于node.js构建微服务中的mock服务

    缘起 由于现在微服务越来越火了,越来越多的微服务融入到了日常开发当中.在开发微服务的时候,经常会遇到一个问题由于依赖于其他服务,导致你的进度受到阻碍.使你不得不先mock出你期望调用依赖服务的输出,来 ...

随机推荐

  1. [软件设计&系统建模] Web软件通用能力模块

    0 基础工具 1 日志 2 权限 3 文件处理(下载/上传) 4 对象池 对象池 数据库连接池 线程池 5 微服务 服务网关 配置中心 注册中心 服务调用 服务熔断 健康检测 Actuator 6 缓 ...

  2. [Spring MVC]@RequestMapping 与 @RequestMapping+@RequestResponse的区别

    假定:返回格式均为JSON,JSON实体对象myJson的属性有:data.message.code.status. 二者的区别在于: @RequestMapping:会在最外层包裹 data属性,将 ...

  3. 【python基础】五大数据类型及常用方法

    1. 数据类型概述 python中的字符串,列表,元组,字典,集合这五种数据类型均是可迭代的,可以使用for循环访问,涵盖了三类数据结构分别为序列.散列.集合. 序列: 字符串 str 列表 list ...

  4. Django笔记二十一之使用原生SQL查询数据库

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十一之使用原生SQL查询数据库 Django 提供了两种方式来执行原生 SQL 代码. 一种是使用 raw() 函数,一种是 使用 co ...

  5. 数组练习 fill sort

    package day05; import java.util.Arrays; //fill sort equals public class testArrays { public static v ...

  6. php对接snmp设备详细讲解

    1.Php安装snmp扩展 1.基础环境准备 Php7.2版本 yum -y install php72w-snmp Php7.4版本 yum install net-snmp php-snmp ne ...

  7. C盘爆满的解决方法,不用删除文件,使用分区助手无损增加内存

    一.分区助手傲梅科技 对于我们C盘内存不足的来说,老师推荐的yyds. 我的内存C盘历史最低是900多M,1.5G还是多的,经过我不断的删除文件,发现没什么用,电脑用久了C盘文件占内存自然就多了!!改 ...

  8. Springboot3整合使用ja-captcha行为验证码解决方案

    截止到目前,Springboot最新稳定版本已经迭代到3.0.5,而我们项目中使用的行为验证码框架ja-captcha还没有适配Springboot3,码云上类似的请求也没有得到过回应,于是决定自己动 ...

  9. 【FAQ】关于JavaScript版本的华为地图服务Map的点击事件与Marker的点击事件存在冲突的解决方案

    一. 问题描述 创建地图对象,并添加marker标记,对map和marker均添加了点击事件: <body> <script> function initMap() { // ...

  10. devops|中小公司效率为王,没必要度量

    之前写过一篇文章<devops|中小公司不要做研发效能度量>,主要是从基础设施方向考虑,因为很多条件都不具备,贸然高投入去做研发效能度量可能达不到我们的预期效果,给出的建议是先做好当下打好 ...