oauth2 authorization code 大致流程
- 用戶打開客戶端以后齿拂,客戶端要求用戶給予授權(quán)睹栖。
- 用戶同意給予客戶端授權(quán)敛瓷。
- 客戶端使用上一步獲得的授權(quán)颜懊,向認(rèn)證服務(wù)器申請令牌河哑。
- 認(rèn)證服務(wù)器對客戶端進(jìn)行認(rèn)證以后避诽,確認(rèn)無誤,同意發(fā)放令牌璃谨。
- 客戶端使用令牌沙庐,向資源服務(wù)器申請獲取資源鲤妥。
- 資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源拱雏。
oauth2 client credentials 大致流程
- 客戶端獲取token
- 客戶端帶上token 可以任意訪問服務(wù)端的接口資源棉安,和用戶無關(guān),token中沒有用戶信息,就是處于未登陸狀態(tài)也可以訪問資源接口
oauth2 password 大致流程
- 客戶端帶上用戶名和密碼獲取token
- 獲取的token是每個用戶自己的token token中含有用戶自己的信息
security oauth2 整合的3個核心配置類
- 資源服務(wù)配置 ResourceServerConfiguration
- 授權(quán)認(rèn)證服務(wù)配置 AuthorizationServerConfiguration
- security 配置 SecurityConfiguration
oauth2 根據(jù)使用場景不同铸抑,分成了4種模式
- 授權(quán)碼模式(authorization code 即先登錄獲取code,再獲取token)
- 簡化模式(implicit 在redirect_uri 的Hash傳遞token; Auth客戶端運行在瀏覽器中,如JS,Flash)
- 密碼模式( password 將用戶名,密碼傳過去,直接獲取token)
- 客戶端模式(client credentials 無用戶,用戶向客戶端注冊,然后客戶端以自己的名義向’服務(wù)端’獲取資源)
demo 中使用了密碼授權(quán)模式 和客戶端授權(quán)模式
工程結(jié)構(gòu)目錄
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 # 應(yīng)用名稱
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
授權(quán)認(rèn)證服務(wù)配置類
@Configuration
@EnableAuthorizationServer // 注解開啟驗證服務(wù)器
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final String CLIEN_ID_ONE = "client_1"; //客戶端1 用來標(biāo)識客戶的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"; // 密碼模式授權(quán)模式
private static final String AUTHORIZATION_CODE = "authorization_code"; //授權(quán)碼模式 授權(quán)碼模式使用到了回調(diào)地址垂券,是最為復(fù)雜的方式,通常網(wǎng)站中經(jīng)常出現(xiàn)的微博羡滑,qq第三方登錄菇爪,都會采用這個形式。
private static final String REFRESH_TOKEN = "refresh_token"; //
private static final String IMPLICIT = "implicit"; //簡化授權(quán)模式
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 = "*"; //指定哪些資源是需要授權(quán)驗證的
@Autowired
private AuthenticationManager authenticationManager; //認(rèn)證方式
@Resource(name = "userService")
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
String secret = new BCryptPasswordEncoder().encode(CLIENT_SECRET); // 用 BCrypt 對密碼編碼
//配置3個個客戶端,一個用于password認(rèn)證柒昏、一個用于client認(rèn)證凳宙、一個用于authorization_code認(rèn)證
configurer.inMemory() // 使用in-memory存儲
.withClient(CLIEN_ID_ONE) //client_id用來標(biāo)識客戶的Id 客戶端1
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes(GRANT_TYPE, REFRESH_TOKEN) //允許授權(quán)類型 客戶端授權(quán)模式
.scopes(SCOPE_READ,SCOPE_WRITE) //允許授權(quán)范圍
.authorities("oauth2") //客戶端可以使用的權(quán)限
.secret(secret) //secret客戶端安全碼
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS) //token 時間秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)//刷新token 時間 秒
.and()
.withClient(CLIEN_ID_TWO) //client_id用來標(biāo)識客戶的Id 客戶端 2
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, REFRESH_TOKEN) //允許授權(quán)類型 密碼授權(quán)模式
.scopes(SCOPE_READ,SCOPE_WRITE) //允許授權(quán)范圍
.authorities("oauth2") //客戶端可以使用的權(quán)限
.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否則根據(jù)refresh_token無法加載用戶信息
.reuseRefreshTokens(true); //開啟刷新token
}
/**
* 認(rèn)證服務(wù)器的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()") //isAuthenticated():排除anonymous isFullyAuthenticated():排除anonymous以及remember-me
.allowFormAuthenticationForClients(); //允許表單認(rèn)證
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("bcrypt");
return converter;
}
@Bean
public TokenStore tokenStore() {
//基于jwt實現(xiàn)令牌(Access Token)
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public ResourceServerTokenServices tokenService() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
資源服務(wù)認(rèn)證配置
@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訪問控制,必須認(rèn)證過后才可以訪問
.and()
.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint()) //認(rèn)證失敗的業(yè)務(wù)處理
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler()); //退出成功的業(yè)務(wù)處理
}
@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 登錄認(rèn)證
@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"));
}
}
認(rèn)證失敗自定義處理
@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.info(" ========================================= 身份認(rèn)證失敗..................... ");
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Access Denied");
}
}
退出系統(tǒng)自定義處理
@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(" ================= 成功退出系統(tǒng) .... ");
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,"退出系統(tǒng)成功."); //自己封裝的代碼 作用就是把信息返回給前端去
}
}
測試 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:授權(quán)端點职祷。
2. /oauth/token:獲取token氏涩。
3. /oauth/confirm_access:用戶確認(rèn)授權(quán)提交端點。
4. /oauth/error:授權(quán)服務(wù)錯誤信息端點有梆。
5. /oauth/check_token:用于資源服務(wù)訪問的令牌解析端點是尖。
6. /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話泥耀。
7. /oauth/logout: 退出
授權(quán)碼模式
client_id:第三方應(yīng)用在授權(quán)服務(wù)器注冊的 Id
response_type:固定值 code饺汹。
redirect_uri:授權(quán)服務(wù)器授權(quán)重定向哪兒的 URL。
scope:權(quán)限
state:隨機(jī)字符串痰催,可以省略
-
訪問連接如果未登陸會跳轉(zhuǎn)到登陸頁面
-
輸入數(shù)據(jù)庫中賬號和密碼登陸后進(jìn)行授權(quán)認(rèn)可界面
點擊“approve” 同意授權(quán)獲取code返回:https://www.baidu.com/?code=lqByMd&state=123
點擊“deny” 拒絕授權(quán) 返回:https://www.baidu.com/?error=access_denied&error_description=User%20denied%20access&state=123
- 授權(quán)之后會得到一個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
如果出現(xiàn)登陸框這輸入賬號: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 參數(shù)逸吵,直接與認(rèn)證服務(wù)器交互,用配置中的客戶端信息去申請accessToken缝裁,客戶端有自己的client_id,client_secret對應(yīng)于用戶的username,password扫皱,而客戶端也擁有自己的authorities,當(dāng)采取client模式認(rèn)證時捷绑,對應(yīng)的權(quán)限也就是客戶端自己的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模式,在認(rèn)證時需要帶上自己的用戶名和密碼胎食,需要傳遞username和password 參數(shù) 扰才,以及客戶端的client_id,client_secret允懂。此時厕怜,accessToken所包含的權(quán)限是用戶本身的權(quán)限,而不是客戶端的權(quán)限。
請求方式:127.0.0.1:18081/oauth/token?username=qiaorulai&password=123456&grant_type=password&client_id=client_2&client_secret=secret
1. username : 系統(tǒng)的登錄名
2. password : 系統(tǒng)的登錄密碼
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: 對應(yīng)簽名獲取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
返回:
正常返回數(shù)據(jù)
攜帶不正確的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地址: