Spring
提供的原生的OAuth2
依賴內置了幾種比較經常使用的受權方式:password
、authorization-code
、client_credentials
、refresh_token
、implicit
等,雖然能夠知足咱們平常的需求,不過針對一些特殊的需求仍是捉襟見肘,有點無奈,好比:微信登陸
、短信登陸
...,針對這一點ApiBoot
經過修改Spring OAuth2
依賴的源碼,能夠根據業務進行自定義添加grantType
。
html
咱們先來使用IDEA
建立本章的項目,pom.xml
添加的依賴以下所示:java
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-security-oauth-jwt</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-mybatis-enhance</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>複製代碼
ApiBoot MyBatis Enhance
使用文檔詳見ApiBoot Mybatis Enhance官網文檔。mysql
本章的源碼在ApiBoot零代碼整合Spring Security的JDBC方式獲取AccessToken基礎上進行修改,將以前章節源碼的application.yml
、SystemUser
、SystemUserEnhanceMppaer
、UserService
文件複製到本章項目對應的目錄內。git
本章來說下使用ApiBoot
怎麼完成自定義短信驗證碼
登陸的受權方式。github
在短信驗證碼登陸的邏輯中,大體的流程以下所示:web
用戶在獲取驗證碼時,系統會將驗證碼保存到數據庫內
當用戶輸入驗證碼後提交登陸時,讀取驗證碼並判斷有效性後
最後獲取手機號對應的用戶信息完成登陸邏輯。
返回請求令牌
根據驗證碼登陸
的流程來看咱們首先須要建立一個驗證碼數據表
,用來保存用戶發送的驗證碼數據,在第3步
中須要經過手機號獲取對應的用戶信息,因此咱們還要修改以前章節建立的表結構,添加一列,下面咱們開始進行改造。spring
在數據庫內建立一個名爲phone_code
的數據表,並初始化一條驗證碼數據(模擬已經用戶已經發送了驗證碼),SQL
以下所示:sql
CREATE TABLE `phone_code` (
`pc_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵自增',
`pc_phone` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '手機號',
`pc_code` varchar(6) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '驗證碼內容',
`pc_create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '驗證碼生成時間',
PRIMARY KEY (`pc_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='手機號驗證碼信息表';
-- 初始化驗證碼數據
INSERT INTO `phone_code` VALUES (1,'17111111111','123123','2019-12-04 03:01:05');複製代碼
對應phone_code
表結構編寫一個數據實體,以下所示:數據庫
/**
* 手機號驗證碼信息表
*
* @author 恆宇少年
*/
@Data
@Table(name = "phone_code")
public class PhoneCode {
/**
* 驗證碼主鍵
*/
@Column(name = "pc_id")
@Id(generatorType = KeyGeneratorTypeEnum.AUTO)
private Integer id;
/**
* 手機號
*/
@Column(name = "pc_phone")
private String phone;
/**
* 驗證碼內容
*/
@Column(name = "pc_code")
private String code;
/**
* 建立時間
*/
@Column(name = "pc_create_time")
private Timestamp createTime;
}複製代碼
爲PhoneCode
驗證碼數據實體添加一個查詢的數據接口,實現ApiBoot MyBatis Enhance
提供的EnhanceMapper
接口,以下所示:api
/**
* 手機號驗證碼數據接口
*
* @author 恆宇少年
*/
public interface PhoneCodeEnhanceMapper extends EnhanceMapper<PhoneCode, Integer> {
/**
* 查詢手機號驗證碼信息
*
* @param phone {@link PhoneCode#getPhone()}
* @param code {@link PhoneCode#getCode()}
* @return {@link PhoneCode}
*/
PhoneCode findByPhoneAndCode(@Param("phone") String phone, @Param("code") String code);
}複製代碼
經過ApiBoot MyBatis Enhance
提供的方法命名規則查詢語法,咱們能夠根據指定的phone
、code
查詢出對應的記錄。
爲驗證碼查詢提供一個業務邏輯實現類,以下所示:
/**
* 驗證碼業務邏輯實現
*
* @author 恆宇少年
*/
@Service
public class PhoneCodeService {
/**
* 手機號驗證碼數據接口
*/
@Autowired
private PhoneCodeEnhanceMapper mapper;
/**
* 查詢手機號驗證碼
*
* @param phone {@link PhoneCode#getPhone()}
* @param code {@link PhoneCode#getCode()}
* @return
*/
public PhoneCode findPhoneCode(String phone, String code) {
return mapper.findByPhoneAndCode(phone, code);
}
}複製代碼
咱們在ApiBoot零代碼整合Spring Security的JDBC方式獲取AccessToken文章內建立的system_user
用戶表的基礎上添加一個字段,以下所示:
alter table system_user
add su_phone varchar(11) null comment '手機號';複製代碼
字段添加後初始化表內yuqiyu
這條數據的列值,我在phone_code
表內添加的手機號爲17111111111
,因此我須要更新su_phone
字段的值爲17111111111
。
基礎的代碼實現咱們都已經準備好了,下面咱們來介紹下本章的主角ApiBootOauthTokenGranter
接口,該接口爲自定義GrantType
而生,由ApiBoot OAuth2
提供,源碼以下所示:
/**
* ApiBoot Integrates Oauth2 to Realize Custom Authorization to Acquire Token
*
* @author:恆宇少年 - 於起宇
* <p>
* DateTime:2019-05-28 09:57
* Blog:http://blog.yuqiyu.com
* WebSite:http://www.jianshu.com/u/092df3f77bca
* Gitee:https://gitee.com/hengboy
* GitHub:https://github.com/hengboy
*/
public interface ApiBootOauthTokenGranter extends Serializable {
/**
* oauth2 grant type for ApiBoot
*
* @return grant type
*/
String grantType();
/**
* load userDetails by parameter
*
* @param parameters parameter map
* @return UserDetails
* @throws ApiBootTokenException
* @see UserDetails
*/
UserDetails loadByParameter(Map<String, String> parameters) throws ApiBootTokenException;
}複製代碼
grantType()
:該方法的返回值用於告知OAuth2
自定義的GrantType
是什麼,根據本身的業務邏輯而定。 loadByParameter
:該方法是自定義GrantType
的業務實現,parameters
參數內包含了自定義受權請求/oauth/token
時所攜帶的所有參數,如:/oauth/token?grant_type=phone_code&phone=xx&code=xx
,會把phone
、code
參數一併傳遞給該方法。 下面咱們來建立一個名爲PhoneCodeGrantType
的自定義受權類,實現ApiBootOauthTokenGranter
接口,以下所示:
/**
* 手機驗證碼OAuth2的認證方式實現
*
* @author 恆宇少年
* @see ApiBootOauthTokenGranter
*/
@Component
public class PhoneCodeGrantType implements ApiBootOauthTokenGranter {
/**
* 手機號驗證碼方式的受權方式
*/
private static final String GRANT_TYPE_PHONE_CODE = "phone_code";
/**
* 受權參數:手機號
*/
private static final String PARAM_PHONE = "phone";
/**
* 受權參數:驗證碼
*/
private static final String PARAM_CODE = "code";
/**
* 手機號驗證碼業務邏輯
*/
@Autowired
private PhoneCodeService phoneCodeService;
/**
* 系統用戶業務邏輯
*/
@Autowired
private UserService userService;
@Override
public String grantType() {
return GRANT_TYPE_PHONE_CODE;
}
/**
* 根據自定義的受權參數進行查詢用戶信息
*
* @param parameters
* @return
* @throws ApiBootTokenException
*/
@Override
public UserDetails loadByParameter(Map<String, String> parameters) throws ApiBootTokenException {
String phone = parameters.get(PARAM_PHONE);
String code = parameters.get(PARAM_CODE);
PhoneCode phoneCode = phoneCodeService.findPhoneCode(phone, code);
if (ObjectUtils.isEmpty(phoneCode)) {
throw new ApiBootTokenException("登陸失敗,驗證碼:" + code + ",已過時.");
}
UserDetails userDetails = userService.findByPhone(phone);
if (ObjectUtils.isEmpty(userDetails)) {
throw new ApiBootTokenException("用戶:" + phone + ",不存在.");
}
return userDetails;
}
}複製代碼
在loadByParameter
方法內,咱們首先獲取到了本次登陸的手機號(phone
)、驗證碼(code
)這兩個參數,查詢是否存在這條驗證碼的記錄(PS:這裏沒作驗證碼過時時間限制,本身的業務請把這塊加上
),驗證碼驗證經過後查詢出手機號對應的用戶信息並將用戶返回交付給ApiBoot OAuth2
框架來完成驗證。
在驗證業務邏輯方法內若是出現異常能夠直接使用ApiBootTokenException
異常進行拋出。
將咱們的項目運行起來,下面經過CURL
的方式嘗試獲取AccessToken
,以下所示:
➜ ~ curl -X POST hengboy:chapter@localhost:9090/oauth/token -d 'grant_type=phone_code&phone=17111111111&code=123123'
{"access_token":"30e3f7d0-8c53-4dfe-b1ff-523a1db7b9eb","token_type":"bearer","refresh_token":"4b1f0ad5-f869-46ca-8b45-0231e69316b3","expires_in":7194,"scope":"api"}複製代碼
使用postman
方式獲取AccessToken
,以下圖所示:
本章根據短信驗證碼登陸
的例子來給你們講解了使用ApiBoot OAuth2
怎麼進行自定義受權方式來獲取AccessToken
,例子講解注重點是在自定義GrantType
,在生產使用時還請根據各類狀況進行驗證,保證數據的安全性。
若是您喜歡本篇文章請爲源碼倉庫點個Star
,謝謝!!!本篇文章示例源碼能夠經過如下途徑獲取,目錄爲apiboot-define-oauth-grant-type
:
做者我的 博客
使用開源框架 ApiBoot 助你成爲Api接口服務架構師