@
OAuth2.0系列博客:
- OAuth2.0系列之基本概念和運作流程(一)
- OAuth2.0系列之授權碼模式實踐教程(二)
- OAuth2.0系列之簡化模式實踐教程(三)
- OAuth2.0系列之密碼模式實踐教程(四)
- OAuth2.0系列之客戶端模式實踐教程(五)
- OAuth2.0系列之信息數據庫存儲教程(六)
- OAuth2.0系列之信息Redis存儲教程(七)
- OAuth2.0系列之JWT令牌實踐教程(八)
- OAuth2.0系列之集成JWT實現單點登錄
1、OAuth2.0信息存儲方式
在前面文章中我們學習了OAuth2的一些基本概念,對OAuth2有了基本的認識,也對OAuth2.0的幾種授權模式進行了實踐,比如授權碼模式
在前面的學習中,我們配置客戶端信息,一般會如下配置,如果有多個客戶端的話,就復制一遍,修改一些參數,很顯然,這種在生產環境是不適應的,所以Spring Security OAuth2是有提供幾種存儲方式的,默認是基於內存存儲
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
// 使用內存存儲
.inMemory()
//標記客戶端id
.withClient(CLIENT_ID)
//客戶端安全碼
.secret(SECRET_CHAR_SEQUENCE)
//為true 直接自動授權成功返回code
.autoApprove(true)
.redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
//允許授權范圍
.scopes(ALL)
//token 時間秒
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
//刷新token 時間 秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
//允許授權類型
.authorizedGrantTypes(GRANT_TYPE_PASSWORD , AUTHORIZATION_CODE , REFRESH_TOKEN , IMPLICIT);
}
對於token存儲一遍使用內存存儲
@Bean
public TokenStore memoryTokenStore() {
// 最基本的InMemoryTokenStore生成token
return new InMemoryTokenStore();
}
IDEA中,Ctrl+Alt+B,可以看到TokenStore的實現,有如下幾種:
ok,其實對於token存儲有如上方式,對於其它比如授權碼code等的存儲也基本如上幾種,分別進行介紹:
- InMemoryTokenStore,默認存儲,保存在內存
- JdbcTokenStore,access_token存儲在數據庫
- JwtTokenStore,JWT這種方式比較特殊,這是一種無狀態方式的存儲,不進行內存、數據庫存儲,只是JWT中攜帶全面的用戶信息,保存在jwt中攜帶過去校驗就可以
- RedisTokenStore,將 access_token 存到 redis 中。
- JwkTokenStore,將 access_token 保存到 JSON Web Key。
2、數據表schema腳本
ok,有了前面的了解之后,現在要實現客戶端配置信息,access_token等等信息的數據庫存儲要怎么實現?
要數據庫存儲,肯定要先建表,當然不用自己設計,去官方github拿就可以。schema腳本:官方Spring-security-oauth2提供的所有腳本:鏈接,官方腳本是HSQL數據庫,所以我們改成mysql版,其中oauth_client_details 用於存儲客戶端配置信息,oauth_code存儲授權碼,oauth_access_token存儲access_token
-- used in tests that use MYSQL
CREATE TABLE oauth_client_details (
client_id VARCHAR(128) PRIMARY KEY,
resource_ids VARCHAR(128),
client_secret VARCHAR(128),
scope VARCHAR(128),
authorized_grant_types VARCHAR(128),
web_server_redirect_uri VARCHAR(128),
authorities VARCHAR(128),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(128)
);
CREATE TABLE oauth_client_token (
token_id VARCHAR(128),
token BLOB,
authentication_id VARCHAR(128) PRIMARY KEY,
user_name VARCHAR(128),
client_id VARCHAR(128)
);
CREATE TABLE oauth_access_token (
token_id VARCHAR(128),
token BLOB,
authentication_id VARCHAR(128) PRIMARY KEY,
user_name VARCHAR(128),
client_id VARCHAR(128),
authentication BLOB,
refresh_token VARCHAR(128)
);
CREATE TABLE oauth_refresh_token (
token_id VARCHAR(128),
token BLOB,
authentication BLOB
);
CREATE TABLE oauth_code (
CODE VARCHAR(128), authentication BLOB
);
CREATE TABLE oauth_approvals (
userId VARCHAR(128),
clientId VARCHAR(128),
scope VARCHAR(128),
STATUS VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- customized oauth_client_details table
CREATE TABLE ClientDetails (
appId VARCHAR(128) PRIMARY KEY,
resourceIds VARCHAR(128),
appSecret VARCHAR(128),
scope VARCHAR(128),
grantTypes VARCHAR(128),
redirectUrl VARCHAR(128),
authorities VARCHAR(128),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(128)
);
要進行登錄驗證,也可以設計一張用戶數據業務表:
# 用戶表
CREATE TABLE USER (
id INT(11) NOT NULL AUTO_INCREMENT,
email VARCHAR(255),
PASSWORD VARCHAR(255) NOT NULL,
phone VARCHAR(255),
username VARCHAR(255) NOT NULL UNIQUE,
last_login_time DATETIME ,
PRIMARY KEY (id)
)
INSERT INTO USER (email,PASSWORD,phone,username,last_login_time) VALUES ('123456@qq.com','11','15522288822','admin',SYSDATE());
3、SpringBoot環境搭建
- IntelliJ IDEA
- Maven3.+版本
新建SpringBoot Initializer項目,可以命名oauth2-jdbc-store
主要加入如下配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- Spring Cloud Security-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
數據庫相關maven配置,orm框架使用mybatis
<!-- springboot mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
<scope>runtime</scope>
</dependency>
lombok也可以加進來,自行配置:
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
數據源配置:
spring:
datasource:
url: jdbc:mysql://127.0.0.1::3306/jeeplatform?autoReconnect=true&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
Mybatis配置類:
package com.example.springboot.oauth2.config;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* <pre>
* Mybatis配置
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/15 15:33 修改內容:
* </pre>
*/
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = {"com.example.springboot.oauth2.mapper"})
public class MybatisConfig {
/**
* 配置支持駝峰命名和大小寫自動轉換
* @Author mazq
* @Date 2020/06/15 15:34
* @Param []
*/
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer(){
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
4、OAuth2.0配置類實現
4.1 客戶端信息
先將客戶端配置信息存儲到數據表:
insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('cms',NULL,'{noop}secret','all','authorization_code','http://localhost:8084/cms/login',NULL,'60','60',NULL,'true');
insert into `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) values('oa',NULL,'{noop}secret','all','authorization_code','http://localhost:8082/oa/login',NULL,'60','60',NULL,'true');
OAuth2.0配置類加上@EnableAuthorizationServer,實現AuthorizationServerConfigurerAdapter抽象類
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
...
}
配置數據從oauth_client_details表讀取來存儲,先加上如下配置
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置數據從oauth_client_details表讀取來存儲
clients.withClientDetails(clientDetailsService());
}
@Bean
public ClientDetailsService clientDetailsService() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcClientDetailsService(dataSource);
}
啟動過程可能出現ClientDetailsService 不能被重寫,可以加上如下配置:
spring:
main:
allow-bean-definition-overriding: true
4.2 授權碼和token配置
授權碼和token同樣配置
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
public TokenStore jdbcTokenStore() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcTokenStore(dataSource);
}
4.3 TokenStoreUserApprovalHandler
自定義TokenStoreUserApprovalHandler:
package com.example.springboot.oauth2.component;
import com.example.springboot.oauth2.entity.dto.OauthClientDetailsDto;
import com.example.springboot.oauth2.service.OAuthService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
/**
* <pre>
* 自定義TokenStoreUserApprovalHandler
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/15 14:48 修改內容:
* </pre>
*/
//@Component
public class JdbcTokenStoreUserApprovalHandler extends TokenStoreUserApprovalHandler {
Logger LOG = LoggerFactory.getLogger(JdbcTokenStoreUserApprovalHandler.class);
@Autowired
OAuthService oAuthService;
@Override
public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
if (super.isApproved(authorizationRequest, userAuthentication)) {
return true;
}
if (!userAuthentication.isAuthenticated()) {
return false;
}
String clientId = authorizationRequest.getClientId();
LOG.info("clientId:[{}]",clientId);
OauthClientDetailsDto clientDetails = oAuthService.loadOauthClientDetails(clientId);
return clientDetails != null;
}
}
OauthClientDetailsDto類:
package com.example.springboot.oauth2.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class OauthClientDetailsDto implements Serializable {
private String clientId;
private String resourceIds;
/**
* Encrypted
*/
private String clientSecret;
/**
* Available values: read,write
*/
private String scope;
/**
* grant types include
* "authorization_code", "password", "assertion", and "refresh_token".
* Default value is "authorization_code,refresh_token".
*/
private String authorizedGrantTypes = "authorization_code,refresh_token";
/**
* The re-direct URI(s) established during registration (optional, comma separated).
*/
private String webServerRedirectUri;
/**
* Authorities that are granted to the client (comma-separated). Distinct from the authorities
* granted to the user on behalf of whom the client is acting.
* <p/>
* For example: ROLE_USER
*/
private String authorities;
/**
* The access token validity period in seconds (optional).
* If unspecified a global default will be applied by the token services.
*/
private Integer accessTokenValidity;
/**
* The refresh token validity period in seconds (optional).
* If unspecified a global default will be applied by the token services.
*/
private Integer refreshTokenValidity;
// optional
private String additionalInformation;
/**
* Value is 'true' or 'false', default 'false'
*/
private String autoApprove;
}
加上UserApprovalHandler bean
@Bean
public OAuth2RequestFactory oAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService());
}
@Bean
public UserApprovalHandler userApprovalHandler() {
JdbcTokenStoreUserApprovalHandler approvalHandler = new JdbcTokenStoreUserApprovalHandler();
approvalHandler.setTokenStore(jdbcTokenStore());
approvalHandler.setClientDetailsService(clientDetailsService());
approvalHandler.setRequestFactory(oAuth2RequestFactory());
return approvalHandler;
}
配置類配置啟動:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jdbcTokenStore()).authenticationManager(authenticationManager)
//自定義AccessToken
//.accessTokenConverter(accessTokenConverter)
//設置userDetailsService
.userDetailsService(userDetailsService)
//授權碼儲存
.authorizationCodeServices(authorizationCodeServices())
//設置userApprovalHandler
.userApprovalHandler(userApprovalHandler())
//設置tokenServices
//.tokenServices(tokenServices())
//支持獲取token方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS)
//刷新token
.reuseRefreshTokens(true);
}
總的OAuth2Config配置類代碼參考:
package com.example.springboot.oauth2.config;
import com.example.springboot.oauth2.component.JdbcTokenStoreUserApprovalHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* <pre>
* OAuth2.0配置類
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/15 10:38 修改內容:
* </pre>
*/
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Resource(name = "userDetailService")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置數據從oauth_client_details表讀取來存儲
clients.withClientDetails(clientDetailsService());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jdbcTokenStore()).authenticationManager(authenticationManager)
//自定義AccessToken
//.accessTokenConverter(accessTokenConverter)
//設置userDetailsService
.userDetailsService(userDetailsService)
//授權碼儲存
.authorizationCodeServices(authorizationCodeServices())
//設置userApprovalHandler
.userApprovalHandler(userApprovalHandler())
//設置tokenServices
//.tokenServices(tokenServices())
//支持獲取token方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS)
//刷新token
.reuseRefreshTokens(true);
}
/**
* 認證服務器的安全配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 開啟/oauth/token_key驗證端口認證權限訪問
.tokenKeyAccess("isAuthenticated()")
// 開啟/oauth/check_token驗證端口認證權限訪問
.checkTokenAccess("isAuthenticated()")
//允許表單認證 在授權碼模式下會導致無法根據code獲取token
.allowFormAuthenticationForClients();
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
public TokenStore jdbcTokenStore() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcTokenStore(dataSource);
}
@Bean
public ClientDetailsService clientDetailsService() {
Assert.state(dataSource != null, "DataSource must be provided");
return new JdbcClientDetailsService(dataSource);
}
@Bean
public OAuth2RequestFactory oAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService());
}
@Bean
public UserApprovalHandler userApprovalHandler() {
JdbcTokenStoreUserApprovalHandler approvalHandler = new JdbcTokenStoreUserApprovalHandler();
approvalHandler.setTokenStore(jdbcTokenStore());
approvalHandler.setClientDetailsService(clientDetailsService());
approvalHandler.setRequestFactory(oAuth2RequestFactory());
return approvalHandler;
}
@Bean
AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService());
services.setSupportRefreshToken(true);
services.setTokenStore(jdbcTokenStore());
return services;
}
}
4.4 自定義UserDetailsService
自定義UserDetailsService:
package com.example.springboot.oauth2.service.impl;
import com.example.springboot.oauth2.entity.User;
import com.example.springboot.oauth2.mapper.UserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
@Service("userDetailService")
public class UserDetailServiceImpl implements UserDetailsService {
Logger LOG = LoggerFactory.getLogger(UserDetailServiceImpl.class);
@Autowired
UserDao userDao;
@Override
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class,readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if(user == null){
//LOG.info("登錄用戶[{}]沒注冊!",username);
throw new UsernameNotFoundException("登錄用戶["+username + "]沒注冊!");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthority());
}
private List getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}
5、SpringSecurity配置類
SpringSecurity配置類:
package com.example.springboot.oauth2.config;
import com.example.springboot.oauth2.component.CustomPasswordEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
/**
* <pre>
* Spring Security配置類
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/15 10:39 修改內容:
* </pre>
*/
@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource(name = "userDetailService")
private UserDetailsService userDetailsService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication()
auth.userDetailsService(userDetailsService)
.passwordEncoder(new CustomPasswordEncoder());
auth.parentAuthenticationManager(authenticationManagerBean());
}
@Override
public void configure(WebSecurity web) throws Exception {
//解決靜態資源被攔截的問題
web.ignoring().antMatchers("/asserts/**");
web.ignoring().antMatchers("/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http // 配置登錄頁並允許訪問
.formLogin().permitAll()
// 配置Basic登錄
//.and().httpBasic()
// 配置登出頁面
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
.and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
// 其余所有請求全部需要鑒權認證
.anyRequest().authenticated()
// 關閉跨域保護;
.and().csrf().disable();
}
}
6、OAuth2.0授權功能簡單測試
訪問授權鏈接,在瀏覽器訪問就可以,授權碼模式response_type參數傳code:
http://localhost:8888/oauth/authorize?client_id=cms&client_secret=secret&response_type=code
因為沒登錄,所以會返回SpringSecurity的默認登錄頁面,具體代碼是 http .formLogin().permitAll();
,如果要彈窗登錄的,可以配置http.httpBasic();
,這種配置是沒有登錄頁面的,自定義登錄頁面可以這樣配置http.formLogin().loginPage("/login").permitAll()
如圖,輸入SpringSecurity配置的數據庫密碼
登錄成功,返回redirect_uri,拿到授權碼
重定向回redirect_uri,http://localhost:8084/cms/login?code=???
查詢數據庫SELECT * FROM oauth_code;
, 授權碼code保存成功:
配置一下請求頭的授權參數,用Basic Auth方式,username即client_id,password即client_secret
拿到授權碼之后去獲取token,本教程使用授權碼方式
查詢數據庫SELECT * FROM oauth_access_token;
,token是保存成功的:
例子代碼下載:code download