spring boot 2.0 整合 security oauth2 password 模式和credentials 模式
oauth2 authorization code 大致流程
- 使用者開啟客戶端以後,客戶端要求使用者給予授權。
- 使用者同意給予客戶端授權。
- 客戶端使用上一步獲得的授權,向認證伺服器申請令牌。
- 認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌。
- 客戶端使用令牌,向資源伺服器申請獲取資源。
- 資源伺服器確認令牌無誤,同意向客戶端開放資源。
oauth2 client credentials 大致流程
- 客戶端獲取token
- 客戶端帶上token 可以任意訪問服務端的介面資源,和使用者無關,token中沒有使用者資訊,就是處於未登陸狀態也可以訪問資源介面
oauth2 password 大致流程
- 客戶端帶上使用者名稱和密碼獲取token
- 獲取的token是每個使用者自己的token token中含有使用者自己的資訊
security oauth2 整合的3個核心配置類
- 資源服務配置 ResourceServerConfiguration
- 授權認證服務配置 AuthorizationServerConfiguration
- security 配置 SecurityConfiguration
oauth2 根據使用場景不同,分成了4種模式
- 授權碼模式(authorization code 即先登入獲取code,再獲取token)
- 簡化模式(implicit 在redirect_uri 的Hash傳遞token; Auth客戶端執行在瀏覽器中,如JS,Flash)
- 密碼模式( password 將使用者名稱,密碼傳過去,直接獲取token)
- 客戶端模式(client credentials 無使用者,使用者向客戶端註冊,然後客戶端以自己的名義向’服務端’獲取資源)
demo 中使用了密碼授權模式 和客戶端授權模式
工程結構目錄
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>>0.9.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.yml
server:
port: 18081
spring:
application:
name: oauth2-server # 應用名稱
jpa:
open-in-view: true
database: POSTGRESQL
show-sql: true
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.PostgreSQLDialect
properties:
hibernate:
temp:
use_jdbc_metadata_defaults: false
datasource:
platform: postgres
url: jdbc:postgresql://127.0.0.1:5432/cloud_oauth2?useUnicode=true&characterEncoding=utf-8
username: postgres
password: postgres123
driver-class-name: org.postgresql.Driver
spring:
redis:
host: 127.0.0.1
database: 0
授權認證服務配置類
@Configuration
@EnableAuthorizationServer // 註解開啟驗證伺服器
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final String CLIEN_ID_ONE = "client_1"; //客戶端1 用來標識客戶的Id
private static final String CLIEN_ID_TWO = "client_2"; //客戶端2
private static final String CLIEN_ID_THREE = "client_3"; //客戶端3
private static final String CLIENT_SECRET = "secret"; //secret客戶端安全碼
private static final String GRANT_TYPE_PASSWORD = "password"; // 密碼模式授權模式
private static final String AUTHORIZATION_CODE = "authorization_code"; //授權碼模式 授權碼模式使用到了回撥地址,是最為複雜的方式,通常網站中經常出現的微博,qq第三方登入,都會採用這個形式。
private static final String REFRESH_TOKEN = "refresh_token"; //
private static final String IMPLICIT = "implicit"; //簡化授權模式
private static final String GRANT_TYPE = "client_credentials"; //客戶端模式
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String TRUST = "trust";
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60*60; //
private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60; //
private static final String RESOURCE_ID = "*"; //指定哪些資源是需要授權驗證的
@Autowired
private AuthenticationManager authenticationManager; //認證方式
@Resource(name = "userService")
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
String secret = new BCryptPasswordEncoder().encode(CLIENT_SECRET); // 用 BCrypt 對密碼編碼
//配置3個個客戶端,一個用於password認證、一個用於client認證、一個用於authorization_code認證
configurer.inMemory() // 使用in-memory儲存
.withClient(CLIEN_ID_ONE) //client_id用來標識客戶的Id 客戶端1
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes(GRANT_TYPE, REFRESH_TOKEN) //允許授權型別 客戶端授權模式
.scopes(SCOPE_READ,SCOPE_WRITE) //允許授權範圍
.authorities("oauth2") //客戶端可以使用的許可權
.secret(secret) //secret客戶端安全碼
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS) //token 時間秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)//重新整理token 時間 秒
.and()
.withClient(CLIEN_ID_TWO) //client_id用來標識客戶的Id 客戶端 2
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, REFRESH_TOKEN) //允許授權型別 密碼授權模式
.scopes(SCOPE_READ,SCOPE_WRITE) //允許授權範圍
.authorities("oauth2") //客戶端可以使用的許可權
.secret(secret) //secret客戶端安全碼
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS) //token 時間秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS); //重新整理token 時間 秒
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) //支援GET POST 請求獲取token
.userDetailsService(userDetailsService) //必須注入userDetailsService否則根據refresh_token無法載入使用者資訊
.reuseRefreshTokens(true); //開啟重新整理token
}
/**
* 認證伺服器的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()") //isAuthenticated():排除anonymous isFullyAuthenticated():排除anonymous以及remember-me
.allowFormAuthenticationForClients(); //允許表單認證
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("bcrypt");
return converter;
}
@Bean
public TokenStore tokenStore() {
//基於jwt實現令牌(Access Token)
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public ResourceServerTokenServices tokenService() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
資源服務認證配置
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "*";
@Autowired
private ResourceServerTokenServices tokenServices;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(true).tokenServices(tokenServices);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/users/**").authenticated() //配置users訪問控制,必須認證過後才可以訪問
.and()
.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint()) //認證失敗的業務處理
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler()); //退出成功的業務處理
}
@Bean
public LogoutSuccessHandler customLogoutSuccessHandler(){
return new CustomLogoutSuccessHandler();
}
@Bean
public AuthenticationEntryPoint customAuthenticationEntryPoint(){
return new CustomAuthenticationEntryPoint();
}
}
Security 配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource(name = "userService")
private UserDetailsService userDetailsService;
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());;
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
security 登入認證
@Service(value = "userService")<br>
public class UserServiceImpl implements UserDetailsService {
@Autowired
private SysAccountRepository repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysAccount user = repository.findByUserAccount(username);
if(user == null){
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getUserAccount(), user.getUserPwd(), getAuthority());
}
private List getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}
認證失敗自定義處理
@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.info(" ========================================= 身份認證失敗..................... ");
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Access Denied");
}
}
退出系統自定義處理
@Slf4j
@Component
public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
@Autowired
private TokenStore tokenStore ;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info(" ================= 成功退出系統 .... ");
String access_token = request.getParameter("access_token");
if(access_token != null){
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(access_token);
log.info("token =" +oAuth2AccessToken.getValue());
tokenStore.removeAccessToken(oAuth2AccessToken);
}
//退出資訊插入日誌記錄表中
ResultUtil.writeJavaScript(httpServletResponse,ErrorCodeEnum.SUCCESS,"退出系統成功."); //自己封裝的程式碼 作用就是把資訊返回給前端去
}
}
測試 controller
@RestController
public class UserController {
@GetMapping(value = "users/list")
public String listUser(){
return "user";
}
@GetMapping(value = "opt/list")
public String optList(){
return "optList";
}
}
請求方式說明
1. /oauth/authorize:授權端點。
2. /oauth/token:獲取token。
3. /oauth/confirm_access:使用者確認授權提交端點。
4. /oauth/error:授權服務錯誤資訊端點。
5. /oauth/check_token:用於資源服務訪問的令牌解析端點。
6. /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話。
7. /oauth/logout: 退出
授權碼模式
client_id:第三方應用在授權伺服器註冊的 Id
response_type:固定值 code。
redirect_uri:授權伺服器授權重定向哪兒的 URL。
scope:許可權
state:隨機字串,可以省略
-
訪問連線如果未登陸會跳轉到登陸頁面
-
輸入資料庫中賬號和密碼登陸後進行授權認可介面
點選“approve” 同意授權獲取code返回:https://www.baidu.com/?code=lqByMd&state=123
點選“deny” 拒絕授權 返回:https://www.baidu.com/?error=access_denied&error_description=User%20denied%20access&state=123
- 授權之後會得到一個code https://www.baidu.com/?code=123456
- 攜帶code獲取token
http://localhost:18082/oauth/token?grant_type=authorization_code&code=123456&client_id=client_3&client_secret=secret&redirect_uri=http://baidu.com
如果出現登陸框這輸入賬號:client_3 密碼 secret 登陸即可獲取token資訊
注意:code 只能用一次,如果失敗需要重新申請
返回:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjE4NjM2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImQ2MjE5NDEyLTkxNDEtNDYyNi1iMjdiLWQ0M2ZhMGFkMTgzMSIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.2hsm_qXloexTLeEb1jtPOF6bIkiNYkBjg_Q2Azs9hxU",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiZDYyMTk0MTItOTE0MS00NjI2LWIyN2ItZDQzZmEwYWQxODMxIiwiZXhwIjoxNTI5MjM2NjM2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImNhYWFmZTg3LWFhYzgtNDNkNC1iOTQyLTFkMDg3MDZhNjU3OSIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.Wb45Uv4aAae0AuSMttHs5XT6pJ45gGXVWUJBiWAU5UI",
"expires_in": 3599,
"scope": "read write",
"jti": "d6219412-9141-4626-b27b-d43fa0ad1831"
}
客戶端模式獲取token
client模式,沒有使用者的概念,不需要傳遞username和password 引數,直接與認證伺服器互動,用配置中的客戶端資訊去申請accessToken,客戶端有自己的client_id,client_secret對應於使用者的username,password,而客戶端也擁有自己的authorities,當採取client模式認證時,對應的許可權也就是客戶端自己的authorities。
client模式 貌似不支援重新整理token請求
1. grant_type : client_credentials client模式固定值
2. client_id : client_1 對於我們註冊客戶端的 client_id 在AuthorizationServerConfiguration配置類中
3. client_secret : secret 對於我們註冊客戶端的 secret 在AuthorizationServerConfiguration配置類中
返回:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjI1NzgwLCJhdXRob3JpdGllcyI6WyJvYXV0aDIiXSwianRpIjoiODQ3MTgwZWMtMzM0OS00NDFiLWFlNGEtNTEwZDE3MTc5ZDY3IiwiY2xpZW50X2lkIjoiY2xpZW50XzEifQ.hVFvyYmmE6emq6G8VP8NxujeAYIiM__0Ivr4pDsbqMY",
"token_type": "bearer",
"expires_in": 3599,
"scope": "read write",
"jti": "847180ec-3349-441b-ae4a-510d17179d67"
}
密碼模式獲取token
password模式,在認證時需要帶上自己的使用者名稱和密碼,需要傳遞username和password 引數 ,以及客戶端的client_id,client_secret。此時,accessToken所包含的許可權是使用者本身的許可權,而不是客戶端的許可權。
請求方式:127.0.0.1:18081/oauth/token?username=qiaorulai&password=123456&grant_type=password&client_id=client_2&client_secret=secret
1. username : 系統的登入名
2. password : 系統的登入密碼
3. grant_type : password 密碼模式固定值
4. client_id : client_2 對於我們註冊客戶端的 client_id 在AuthorizationServerConfiguration配置類中
5. client_secret : secret 對於我們註冊客戶端的 secret 在AuthorizationServerConfiguration配置類中
返回:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjE4NjM2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImQ2MjE5NDEyLTkxNDEtNDYyNi1iMjdiLWQ0M2ZhMGFkMTgzMSIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.2hsm_qXloexTLeEb1jtPOF6bIkiNYkBjg_Q2Azs9hxU",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiZDYyMTk0MTItOTE0MS00NjI2LWIyN2ItZDQzZmEwYWQxODMxIiwiZXhwIjoxNTI5MjM2NjM2LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImNhYWFmZTg3LWFhYzgtNDNkNC1iOTQyLTFkMDg3MDZhNjU3OSIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.Wb45Uv4aAae0AuSMttHs5XT6pJ45gGXVWUJBiWAU5UI",
"expires_in": 3599,
"scope": "read write",
"jti": "d6219412-9141-4626-b27b-d43fa0ad1831"
}
密碼模式重新整理token
1. grant_type : password 密碼模式固定值
2. client_id : client_2 對於我們註冊客戶端的 client_id 在AuthorizationServerConfiguration配置類中
3. client_secret : secret 對於我們註冊客戶端的 secret 在AuthorizationServerConfiguration配置類中
4. refresh_token: 對應簽名獲取token中的refresh_token值
返回:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjE4NzM5LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjAzMjc5NDM5LWNjYjYtNGI3My1iODM4LTgwNTA5YjVkNWFjNiIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.vwXCxlLRKLbqnWm6HuqAVO0j2YzSn1oHQ-GX4LZkEx8",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiMDMyNzk0MzktY2NiNi00YjczLWI4MzgtODA1MDliNWQ1YWM2IiwiZXhwIjoxNTI5MjI4MDUxLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjVkNGVmMmNlLTc3MzEtNGVkYS1iZjFmLTkxZWVjYzk4YjQyOCIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.8ihBYB99dae5MTGrWqXlJGntj4wVr8i6FNCqqAbtwdo",
"expires_in": 3599,
"scope": "read write",
"jti": "03279439-ccb6-4b73-b838-80509b5d5ac6"
}
不攜帶token訪問介面
請求:127.0.0.1:18081/users/list
返回:
{
"timestamp": "2018-06-17T09:26:17.707+0000",
"status": 401,
"error": "Unauthorized",
"message": "Access Denied",
"path": "/users/list"
}
攜帶正確的token 訪問介面
請求:127.0.0.1:18081/users/list?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjMxMzE3LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6Ijc1Mzg3OWMyLTI4ZDktNDgxMy04YTAxLWZkNzQ4OGNlOWRkMCIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.V9I2lBYKk7sNsygj_bwrJZF06A8LhZx2x_MHmapppGE
返回:
正常返回資料
攜帶不正確的token 訪問介面
請求:127.0.0.1:18081/users/list?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjMxMzE3LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6Ijc1Mzg3OWMyLTI4ZDktNDgxMy04YTAxLWZkNzQ4OGNlOWRkMCIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.V9I2lBYKk7sNsygj_bwrJZF06A8LhZx2x_MHmapppGE
返回:
{
"error": "invalid_token",
"error_description": "Access token expired: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiKiJdLCJ1c2VyX25hbWUiOiJxaWFvcnVsYWkiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTI5MjE4NzM5LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjAzMjc5NDM5LWNjYjYtNGI3My1iODM4LTgwNTA5YjVkNWFjNiIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.vwXCxlLRKLbqnWm6HuqAVO0j2YzSn1oHQ-GX4LZkEx8"
demo地址:
相關文章
- spring security oauth2 password授權模式SpringOAuth模式
- spring boot 2.0 整合 oauth2 authorization code授權碼模式Spring BootOAuth模式
- Spring Boot Security 整合 OAuth2 設計安全API介面服務Spring BootOAuthAPI
- Spring Boot整合Spring SecuritySpring Boot
- Spring Security原始碼分析十一:Spring Security OAuth2整合JWTSpring原始碼OAuthJWT
- spring-boot 整合 spring-securitySpringboot
- Spring Boot整合Redis叢集(Cluster模式)Spring BootRedis模式
- Spring Boot 2.0(八):Spring Boot 整合 MemcachedSpring Boot
- Spring Security如何優雅的增加OAuth2協議授權模式SpringOAuth協議模式
- Spring Security Oauth2SpringOAuth
- Spring security(四)-spring boot +spring security簡訊認證+redis整合Spring BootRedis
- Spring Boot 整合 Spring Security 入門案例教程Spring Boot
- Spring Boot 策略模式Spring Boot模式
- Spring Boot(十三):整合Redis哨兵,叢集模式實踐Spring BootRedis模式
- Spring Boot 3中將JWT與Spring Security 6整合Spring BootJWT
- 五、Spring Boot整合Spring Security之認證流程2Spring Boot
- Spring security OAuth2 深入解析SpringOAuth
- Spring Boot SecuritySpring Boot
- Spring Boot —— Spring SecuritySpring Boot
- Spring Cloud :: Security :: OAuth2 – ⓪ OAuth2模型詳述SpringCloudOAuth模型
- Spring Security原始碼分析十:初識Spring Security OAuth2Spring原始碼OAuth
- Spring Security OAuth 2.0SpringOAuth
- spring-boot spring-security oauth2 /oauth/token報401,403 問題SpringbootOAuth
- Spring Boot Security OAuth2 實現支援JWT令牌的授權伺服器Spring BootOAuthJWT伺服器
- 暢購商城(九):Spring Security Oauth2SpringOAuth
- Spring Security OAuth2 單點登入SpringOAuth
- Spring Boot 整合 Mybatis 和 MySQLSpring BootMyBatisMySql
- Activiti7 與 Spring Boot 及 Spring Security 整合 踩坑記錄Spring Boot
- Spring Boot Security 詳解Spring Boot
- Spring Boot Security配置教程Spring Boot
- 六、Spring Boot整合Spring Security之前後分離認證流程最佳方案Spring Boot
- 七、Spring Boot整合Spring Security之前後分離認證最佳實現Spring Boot
- Spring Cloud Security:Oauth2結合JWT使用SpringCloudOAuthJWT
- spring security oauth2 搭建認證中心demoSpringOAuth
- Spring Boot 2.0(二):Spring Boot 2.0嚐鮮-動態 BannerSpring Boot
- Api架構奧義:ApiBoot實現零程式碼整合Spring Security & OAuth2API架構bootSpringOAuth
- Spring Security(一):整合JWTSpringJWT
- Spring Security 的 HttpBasic模式 活該被放棄SpringHTTP模式