# Spring Cloud下基于OAUTH2認證授權的實現示例
在`Spring Cloud`需要使用`OAUTH2`來實現多個微服務的統一認證授權,通過向`OAUTH服務`發送某個類型的`grant type`進行集中認證和授權,從而獲得`token`,而這個token是受其他微服務信任的,我們在后續的訪問可以通過`token`來進行,從而實現了微服務的統一認證授權。
**本示例提供了四大部分:**
1. `discovery-service`:服務注冊和發現的基本模塊
2. `auth-server`:OAUTH2認證授權中心
3. `order-service`:普通微服務,用來驗證認證和授權
4. `api-gateway`:邊界網關(所有微服務都在它之后)
**OAUTH2中的角色:**
1. `Resource Server`:被授權訪問的資源
2. `Authotization Server`:OAUTH2認證授權中心
3. `Resource Owner`: 用戶
4. `Client`:使用API的客戶端(如Android 、IOS、web app)
Grant Type:
1. `Authorization Code`:用在服務端應用之間
2. `Implicit`:用在移動app或者web app(這些app是在用戶的設備上的,如在手機上調起微信來進行認證授權)
3. `Resource Owner Password Credentials(password)`:應用直接都是受信任的(都是由一家公司開發的,本例子使用
4. `Client Credentials`:用在應用API訪問。
5. 【擴展pwd】MobilePassword 手機號密碼
6. 【擴展sms】MobileSms 手機號短信驗證碼
環境搭建:
1、基礎環境
```
mysql oauth\_client\_details 表存儲客戶端信息
redis 存儲短信驗證碼信息
```
2、auth-server搭建
*****
\*\*2.1 \*\*定義攔截器攔截登錄的請求【在這個類種主要完成2部分工作:1、根據參數獲取當前的是認證類型,2、根據不同的認證類型調用不同的IntegrationAuthenticator.prepar進行預處理】
~~~
package com.hjf.auth.filter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 功能說明:【】
* 作 者:lihaijun
* 創建日期:2020-11-20
* Spring Security對于獲取TOKEN的請求(默認是"/oauth/token"),需要認證client_id和client_secret。
* 認證client_id和client_secret可以有2種方式,一種是通過本節講的ClientCredentialsTokenEndpointFilter,
* 另一種是通過BasicAuthenticationFilter。
* ClientCredentialsTokenEndpointFilter首先比對請求URL是否是TOKEN請求路徑以及請求參數中是否包含
* client_id,如果滿足以上條件,再調用ProviderManager認證client_id和client_secret是否與配置的一致。
* 如果通過認證,會把身份認證信息保存打SecurityContext上下文中。
*/
public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
private AuthorizationServerSecurityConfigurer configurer;
private AuthenticationEntryPoint authenticationEntryPoint;
public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer, String path) {
this.configurer = configurer;
setFilterProcessesUrl(path);
}
@Override
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
// 把父類的干掉
super.setAuthenticationEntryPoint(null);
this.authenticationEntryPoint = authenticationEntryPoint;
}
@Override
protected AuthenticationManager getAuthenticationManager() {
return configurer.and().getSharedObject(AuthenticationManager.class);
}
@Override
public void afterPropertiesSet() {
setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
authenticationEntryPoint.commence(httpServletRequest, httpServletResponse, e);
}
});
setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
// 無操作-僅允許過濾器鏈繼續到令牌端點
}
});
}
}
~~~
*****
**2.2 OAuth2服務配置 【將攔截器放入到攔截鏈條中】**
~~~
package com.hjf.auth.configurer;
/**
* 功能說明:【授權服務配置】
* 作 者:lihaijun
* 創建日期:2020-11-09
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
@Autowired private TokenService tokenService;
@Autowired private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired private CustomUserDetailsService userDetailsService;
@Autowired private AuthorizationServerEndpointsConfiguration configuration;
@Resource private DataSource dataSource;
@Resource private AuthenticationManager authenticationManager;
/**
* 配置客戶端詳情
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetails());
}
/**
* 配置令牌端點得訪問約束
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
String path = "/oauth/token";
try {
// 獲取自定義映射路徑,比如 ((AuthorizationServerEndpointsConfigurer) endpoints).pathMapping("/oauth/token", "/my/token");
path = configuration.oauth2EndpointHandlerMapping().getServletPath(path);
} catch (Exception e) {
}
CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security, path);
endpointFilter.afterPropertiesSet();
endpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
security.authenticationEntryPoint(authenticationEntryPoint);
security.addTokenEndpointAuthenticationFilter(endpointFilter);
security.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("permitAll()");
}
/**
* 配置令牌訪問端點、令牌服務
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenService.tokenStore());
List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getAuthorizationCodeServices(), endpoints.getTokenStore(), endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
endpoints.tokenEnhancer(tokenService.tokenEnhancer());
}
/**
* 授權模式【用戶名密碼、Token刷新、授權碼、手機號密碼、手機號驗證碼】
* @param authorizationCodeServices
* @param tokenStore
* @param tokenServices
* @param clientDetailsService
* @param requestFactory
*/
private List<TokenGranter> getTokenGranters(AuthorizationCodeServices authorizationCodeServices, TokenStore tokenStore, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
return new ArrayList<>(Arrays.asList(
new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory),
new CustomRefreshTokenGranter(tokenStore, tokenServices, clientDetailsService, requestFactory),
new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory),
new MobilePasswordCustomTokenGranter(userDetailsService, tokenServices, clientDetailsService, requestFactory),
new MobileSmsCustomTokenGranter(userDetailsService, tokenServices, clientDetailsService, requestFactory)
));
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
}
~~~
*****
**2.3web安全配置**
~~~
package com.hjf.auth.configurer;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 功能說明:【安全配置】
* 作 者:lihaijun
* 創建日期:2020-11-09
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.antMatchers("/oauth/**").permitAll()
.anyRequest().permitAll()
.and()
.csrf().disable();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
~~~
**2.4 自定義令牌授予者【手機號+驗證碼等】**
~~~
package com.hjf.auth.granter;
import com.hjf.auth.model.MyUserDetails;
import com.hjf.auth.service.CustomUserDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import java.util.Map;
/**
* 功能說明:【自定義手機號+驗證碼令牌授予者】
* 作 者:lihaijun
* 創建日期:2020-11-09
*/
public class MobileSmsCustomTokenGranter extends AbstractCustomTokenGranter {
protected CustomUserDetailsService userDetailsService;
public MobileSmsCustomTokenGranter(CustomUserDetailsService userDetailsService, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
super(tokenServices, clientDetailsService, requestFactory, "sms");
this.userDetailsService = userDetailsService;
}
@Override
protected MyUserDetails getCustomUser(Map<String, String> parameters) {
String mobile = parameters.get("mobile");
String smscode = parameters.get("smscode");
return userDetailsService.loadUserByMobileAndSmsCode(mobile, smscode);
}
}
~~~
**2.5 用戶細節服務實現 【具體實現根據認證類型進行用戶數據查詢操作】**
~~~
package com.hjf.auth.service;
import com.hjf.api.entity.Account;
import com.hjf.auth.constant.CacheKeys;
import com.hjf.auth.constant.MessageConstant;
import com.hjf.auth.mapper.AccountMapper;
import com.hjf.auth.model.MyUserDetails;
import com.hjf.core.cache.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
/**
* 功能說明:【用戶細節服務實現】
* 作 者:lihaijun
* 創建日期:2020-11-20
*/
@Slf4j
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Resource RedisUtil redisUtil;
@Resource AccountMapper accountMapper;
/**
* 手機號密碼模式驗證邏輯
*/
public MyUserDetails loadUserByMobileAndPassword(String mobile, String password) {
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new InvalidGrantException("您輸入的手機號或密碼不正確");
}
Account account=accountMapper.getByTelephone(mobile);
if (account==null){
throw new UsernameNotFoundException(MessageConstant.LOGIN_ERROR_ACCOUNT_NOTFOUND);
}
MyUserDetails userDetails=new MyUserDetails(account);
return userDetails;
}
/**
* 手機號和驗證碼驗證邏輯
*/
public MyUserDetails loadUserByMobileAndSmsCode(String mobile, String smsCode) {
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(smsCode)) {
log.error("【根據手機號和驗證碼查詢用戶信息】 缺失手機號或者驗證碼");
throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE);
}
Object smsCodeCache=redisUtil.get(CacheKeys.SMS_CODE+mobile);
if (smsCodeCache==null){
log.error("【根據手機號和驗證碼查詢用戶信息】 沒有查詢到緩存數據");
throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE);
}
if (!smsCode.equals(smsCodeCache.toString())){
log.error("【根據手機號和驗證碼查詢用戶信息】 緩存查詢到的信息和輸入信息不匹配");
throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE);
}
Account account=accountMapper.getByTelephone(mobile);
if (account==null){
log.error("【根據手機號和驗證碼查詢用戶信息】 數據庫沒有查詢到賬戶信息");
throw new UsernameNotFoundException(MessageConstant.LOGIN_ERROR_ACCOUNT_NOTFOUND);
}
MyUserDetails userDetails=new MyUserDetails(account);
return userDetails;
}
/**
* 用戶名&密碼驗證邏輯【系統ResourceOwnerPasswordTokenGranter使用的默認功能 】
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String[] permissionArray = new String[]{};
Account account=accountMapper.getByTelephone(username);
if (account==null){
log.error("【用戶名&密碼認證】");
throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR);
}
MyUserDetails securityUser = new MyUserDetails(account);
securityUser.setPassword(passwordEncoder.encode(account.getPassword()));
securityUser.setEnabled(true);
return securityUser;
}
}
~~~
**2.6 token服務工具【tokenEnhancer、tokenStore、jwtAccessTokenConverter】**
~~~
package com.hjf.auth.token;
import com.hjf.auth.model.MyUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.security.KeyPair;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
/**
* 功能說明:【Token服務】
* 作 者:lihaijun
* 創建日期:2020-11-20
*/
@Component
public class TokenService {
@Autowired private RedisConnectionFactory redisFactory;
private TokenStoreType tokenStoreType = TokenStoreType.JWT;
private enum TokenStoreType {
IN_MEMORY, REDIS, JWT
}
@Bean
public TokenStore tokenStore() {
switch (tokenStoreType) {
case REDIS:
return new RedisTokenStore(redisFactory);
case JWT:
return new JwtTokenStore(jwtAccessTokenConverter());
}
return new InMemoryTokenStore();
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new TokenEnhancer() {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) oAuth2AccessToken;
MyUserDetails user = (MyUserDetails) oAuth2Authentication.getPrincipal();
Map<String, Object> map = new LinkedHashMap<>();
map.put("username", user.getUsername());
map.put("telephone", user.getTelephone());
map.put("accountId", user.getAccountId());
token.setAdditionalInformation(map);
if (tokenStoreType == TokenStoreType.JWT) {
token = (DefaultOAuth2AccessToken) jwtAccessTokenConverter().enhance(oAuth2AccessToken, oAuth2Authentication);
} else {
token.setValue(buildTokenValue());
if (token.getRefreshToken() != null) {
if (token.getRefreshToken() instanceof DefaultExpiringOAuth2RefreshToken) {
DefaultExpiringOAuth2RefreshToken refreshToken = (DefaultExpiringOAuth2RefreshToken) token.getRefreshToken();
token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken(buildTokenValue(), refreshToken.getExpiration()));
} else {
token.setRefreshToken(new DefaultOAuth2RefreshToken(buildTokenValue()));
}
}
}
return token;
}
};
}
@Bean
public KeyPair keyPair() {
//從classpath下的證書中獲取秘鑰對
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
// accessTokenConverter.setSigningKey("demo-oauth2");
((DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(new DefaultUserAuthenticationConverter() {
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
MyUserDetails customUser = new MyUserDetails();
BeanMap.create(customUser).putAll(map);
Object authorities = map.get("authorities");
if (authorities instanceof String) {
customUser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities));
} else if (authorities instanceof Collection) {
customUser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.collectionToCommaDelimitedString((Collection) authorities)));
}
return new PreAuthenticatedAuthenticationToken(customUser, null, customUser.getAuthorities());
}
});
accessTokenConverter.setKeyPair(keyPair());
return accessTokenConverter;
}
public String buildTokenValue() {
String tokenValue = UUID.randomUUID().toString() + UUID.randomUUID().toString();
return tokenValue;
}
}
~~~
**2.7 token生成入口**
~~~
package com.hjf.auth.controller;
import com.hjf.auth.token.Oauth2TokenDto;
import com.hjf.frame.base.BaseResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.Map;
/**
* 功能說明:【認證服務入口】
* 作 者:lihaijun
* 創建日期:2020-11-20
*/
@RestController
@RequestMapping("/oauth")
public class OauthController {
@Autowired
private TokenEndpoint tokenEndpoint;
@GetMapping("/token")
public BaseResp getAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
return resp(tokenEndpoint.getAccessToken(principal, parameters).getBody());
}
@PostMapping("/token")
public BaseResp postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
return resp(tokenEndpoint.postAccessToken(principal, parameters).getBody());
}
/**
* 定制申請返回實體
*/
private BaseResp resp(OAuth2AccessToken accessToken) {
Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder()
.token(accessToken.getValue())
.refreshToken(accessToken.getRefreshToken().getValue())
.expiresIn(accessToken.getExpiresIn())
.tokenHead("Bearer ")
.additionalInformation(accessToken.getAdditionalInformation())
.build();
return BaseResp.success(oauth2TokenDto);
}
}
~~~
*****
測試 localhost:8000/oauth/token?client\_id=client-mp&client\_secret=123456&mobile=15010123237&password=123456&smscode=1111&grant\_type=sms

- 項目介紹
- 項目聲明
- 項目簡介
- 架構設計
- 項目亮點功能介紹
- 技術棧介紹
- 核心功能
- 運行環境
- 項目更新日志
- 文檔更新日志
- F&Q
- 部署教程
- 環境準備
- JDK安裝
- JDK1.8,17共存
- maven
- 分布式緩存Redis
- 單機版
- 集群
- 注冊&配置中心alibaba/nacos
- 介紹
- Nacos安裝
- Nacos配置中心
- Nacos注冊發現
- Nacos生產部署方案
- 服務監控-BootAdmin
- 基本介紹
- 如何使用
- 整合Admin-Ui
- 客戶端配置
- 鏈路追蹤
- 基本介紹
- SkyWalking-1
- Skywalking-1
- 消息隊列
- Kafka
- docker安裝kafka
- Linux集群
- Maven私服
- nexus安裝部署
- nexus使用介紹
- 全文搜索elasticsearch
- windows集群搭建
- docker安裝es
- ElasticHD
- linux集群部署
- 統一日志解決方案
- 日志解決方案設計
- 介紹與相關資料
- ELK安裝部署
- elasticsearch 7.5
- logstash-7.5
- kibana-7.5
- filebeat
- 服務監控-Prometheus
- Prometheus安裝配置
- Prometheus介紹
- grafana
- 持續集成部署CICD
- 自動化部署Jenkins
- 安裝部署win
- 打包發布遠程執行
- 安裝部署linux
- jenkins+gitlab+docker容器化工程自動化部署
- Git
- CICD說明
- 阿里云效
- CentOS_MYSQL安裝
- docker
- 安裝
- Docker安裝Nginx
- Docker部署啟動springboot
- dockerCompose
- harbor
- Docker私有鏡像倉庫
- Portainer
- Docker遠程連接設置
- 打包工程
- 必要啟動模塊
- 核心模塊
- 登錄認證
- 緩存功能
- 日志模塊
- 分布式鎖
- 消息隊列
- 異常處理
- 系統接口
- 參數驗證
- es檢索
- 數據導出
- 系統設計
- 系統總體架構
- 擴展模塊(可選)
- 限流熔斷alibaba/sentinel
- 使用Sentinel實現gateway網關及服務接口限流
- Sentinel使用Nacos存儲規則及同步
- 服務調用Feign
- Feign基本介紹
- 如何使用
- 負載均衡
- 請求超時
- 請求攔截器
- 分布式任務調度
- XXL-JOB
- 分布式事務
- TX-LCN
- Seata
- Seata原理解析
- 數據庫分庫分表
- swagger文檔
- 分布式ID生成器解決方案
- 服務網關CloudGateway
- 基本介紹
- 使用網關
- 路由配置
- 全局過濾器
- 服務認證授權架構設計
- 認證服務流程
- 授權服務流程
- 系統冪等性設計與實踐
- 分布式日志鏈路跟蹤
- 實時搜索系統設計
- 應用性能
- 壓力測試工具
- Apache JMeter介紹和安裝
- ApacheJMeter使用
- JVM
- JVM性能調優
- 常見JVM內存錯誤及解決方案
- JVM 分析工具詳解
- Spring Cloud性能調優
- Linux運維
- Linux 常用命令
- Linux開啟端口