SpringSecurity & OAuth2實現(xiàn)短信驗證碼方式獲取AccessToken

Spring提供的原生的OAuth2依賴內(nèi)置了幾種比較常用的授權(quán)方式:password媒区、authorization-code磨确、client_credentials沽甥、refresh_tokenimplicit等乏奥,雖然可以滿足我們?nèi)粘5男枨螅贿^針對一些特殊的需求還是捉襟見肘亥曹,有點無奈邓了,比如:微信登錄短信登錄...媳瞪,針對這一點ApiBoot通過修改Spring OAuth2依賴的源碼骗炉,可以根據(jù)業(yè)務(wù)進行自定義添加grantType

ApiBoot Security OAuth組件系列文章

創(chuàng)建項目

我們先來使用IDEA創(chuàng)建本章的項目,pom.xml添加的依賴如下所示:

<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官網(wǎng)文檔把将。

本章的源碼在ApiBoot零代碼整合Spring Security的JDBC方式獲取AccessToken基礎(chǔ)上進行修改轻专,將之前章節(jié)源碼的application.ymlSystemUser察蹲、SystemUserEnhanceMppaer请垛、UserService文件復(fù)制到本章項目對應(yīng)的目錄內(nèi)。

驗證碼登錄邏輯

本章來講下使用ApiBoot怎么完成自定義短信驗證碼登錄的授權(quán)方式洽议。

在短信驗證碼登錄的邏輯中宗收,大致的流程如下所示:

  1. 用戶在獲取驗證碼時,系統(tǒng)會將驗證碼保存到數(shù)據(jù)庫內(nèi)
  2. 當(dāng)用戶輸入驗證碼后提交登錄時亚兄,讀取驗證碼并判斷有效性后
  3. 最后獲取手機號對應(yīng)的用戶信息完成登錄邏輯混稽。
  4. 返回請求令牌

根據(jù)驗證碼登錄的流程來看我們首先需要創(chuàng)建一個驗證碼數(shù)據(jù)表,用來保存用戶發(fā)送的驗證碼數(shù)據(jù),在第3步中需要通過手機號獲取對應(yīng)的用戶信息荚坞,所以我們還要修改之前章節(jié)創(chuàng)建的表結(jié)構(gòu)挑宠,添加一列,下面我們開始進行改造颓影。

驗證碼表結(jié)構(gòu)

在數(shù)據(jù)庫內(nèi)創(chuàng)建一個名為phone_code的數(shù)據(jù)表各淀,并初始化一條驗證碼數(shù)據(jù)(模擬已經(jīng)用戶已經(jīng)發(fā)送了驗證碼),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 '驗證碼內(nèi)容',
  `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='手機號驗證碼信息表';
-- 初始化驗證碼數(shù)據(jù)
INSERT INTO `phone_code` VALUES (1,'17111111111','123123','2019-12-04 03:01:05');

驗證碼實體

對應(yīng)phone_code表結(jié)構(gòu)編寫一個數(shù)據(jù)實體诡挂,如下所示:


/**
 * 手機號驗證碼信息表
 *
 * @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;
    /**
     * 驗證碼內(nèi)容
     */
    @Column(name = "pc_code")
    private String code;
    /**
     * 創(chuàng)建時間
     */
    @Column(name = "pc_create_time")
    private Timestamp createTime;
}

驗證碼數(shù)據(jù)接口

PhoneCode驗證碼數(shù)據(jù)實體添加一個查詢的數(shù)據(jù)接口碎浇,實現(xiàn)ApiBoot MyBatis Enhance提供的EnhanceMapper<Entity,ID>接口,如下所示:

/**
 * 手機號驗證碼數(shù)據(jù)接口
 *
 * @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提供的方法命名規(guī)則查詢語法璃俗,我們可以根據(jù)指定的phone奴璃、code查詢出對應(yīng)的記錄。

驗證碼業(yè)務(wù)邏輯

為驗證碼查詢提供一個業(yè)務(wù)邏輯實現(xiàn)類城豁,如下所示:

/**
 * 驗證碼業(yè)務(wù)邏輯實現(xiàn)
 *
 * @author 恒宇少年
 */
@Service
public class PhoneCodeService {
    /**
     * 手機號驗證碼數(shù)據(jù)接口
     */
    @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);
    }
}

修改用戶表結(jié)構(gòu)

我們在ApiBoot零代碼整合Spring Security的JDBC方式獲取AccessToken文章內(nèi)創(chuàng)建的system_user用戶表的基礎(chǔ)上添加一個字段苟穆,如下所示:

alter table system_user
    add su_phone varchar(11) null comment '手機號';

字段添加后初始化表內(nèi)yuqiyu這條數(shù)據(jù)的列值,我在phone_code表內(nèi)添加的手機號為17111111111唱星,所以我需要更新su_phone字段的值為17111111111雳旅。

了解ApiBootOauthTokenGranter

基礎(chǔ)的代碼實現(xiàn)我們都已經(jīng)準(zhǔn)備好了,下面我們來介紹下本章的主角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.reibang.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是什么哎榴,根據(jù)自己的業(yè)務(wù)邏輯而定型豁。
  • loadByParameter:該方法是自定義GrantType的業(yè)務(wù)實現(xiàn),parameters參數(shù)內(nèi)包含了自定義授權(quán)請求/oauth/token時所攜帶的全部參數(shù)尚蝌,如:/oauth/token?grant_type=phone_code&phone=xx&code=xx迎变,會把phonecode參數(shù)一并傳遞給該方法驼壶。

實現(xiàn)短信驗證碼授權(quán)方式

下面我們來創(chuàng)建一個名為PhoneCodeGrantType的自定義授權(quán)類氏豌,實現(xiàn)ApiBootOauthTokenGranter接口,如下所示:


/**
 * 手機驗證碼OAuth2的認證方式實現(xiàn)
 *
 * @author 恒宇少年
 * @see ApiBootOauthTokenGranter
 */
@Component
public class PhoneCodeGrantType implements ApiBootOauthTokenGranter {
    /**
     * 手機號驗證碼方式的授權(quán)方式
     */
    private static final String GRANT_TYPE_PHONE_CODE = "phone_code";
    /**
     * 授權(quán)參數(shù):手機號
     */
    private static final String PARAM_PHONE = "phone";
    /**
     * 授權(quán)參數(shù):驗證碼
     */
    private static final String PARAM_CODE = "code";
    /**
     * 手機號驗證碼業(yè)務(wù)邏輯
     */
    @Autowired
    private PhoneCodeService phoneCodeService;
    /**
     * 系統(tǒng)用戶業(yè)務(wù)邏輯
     */
    @Autowired
    private UserService userService;

    @Override
    public String grantType() {
        return GRANT_TYPE_PHONE_CODE;
    }

    /**
     * 根據(jù)自定義的授權(quán)參數(shù)進行查詢用戶信息
     *
     * @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方法內(nèi)般妙,我們首先獲取到了本次登錄的手機號(phone)纪铺、驗證碼(code)這兩個參數(shù),查詢是否存在這條驗證碼的記錄(PS:這里沒做驗證碼過期時間限制碟渺,自己的業(yè)務(wù)請把這塊加上)鲜锚,驗證碼驗證通過后查詢出手機號對應(yīng)的用戶信息并將用戶返回交付給ApiBoot OAuth2框架來完成驗證。

在驗證業(yè)務(wù)邏輯方法內(nèi)如果出現(xiàn)異常可以直接使用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骏令,如下圖所示:

敲黑板蔬捷,劃重點

本章根據(jù)短信驗證碼登錄的例子來給大家講解了使用ApiBoot OAuth2怎么進行自定義授權(quán)方式來獲取AccessToken,例子講解注重點是在自定義GrantType榔袋,在生產(chǎn)使用時還請根據(jù)各種情況進行驗證周拐,保證數(shù)據(jù)的安全性。

代碼示例

如果您喜歡本篇文章請為源碼倉庫點個Star凰兑,謝謝M姿凇!吏够!
本篇文章示例源碼可以通過以下途徑獲取勾给,目錄為apiboot-define-oauth-grant-type

作者個人 博客
使用開源框架 ApiBoot 助你成為Api接口服務(wù)架構(gòu)師

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锅知,隨后出現(xiàn)的幾起案子锦秒,更是在濱河造成了極大的恐慌,老刑警劉巖喉镰,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惭笑,居然都是意外死亡侣姆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門沉噩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捺宗,“玉大人,你說我怎么就攤上這事川蒙⊙晾鳎” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵畜眨,是天一觀的道長昼牛。 經(jīng)常有香客問我,道長康聂,這世上最難降的妖魔是什么贰健? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮恬汁,結(jié)果婚禮上伶椿,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好脊另,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布导狡。 她就那樣靜靜地躺著,像睡著了一般偎痛。 火紅的嫁衣襯著肌膚如雪旱捧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天看彼,我揣著相機與錄音廊佩,去河邊找鬼。 笑死靖榕,一個胖子當(dāng)著我的面吹牛标锄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茁计,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼料皇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了星压?” 一聲冷哼從身側(cè)響起践剂,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎娜膘,沒想到半個月后逊脯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡竣贪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年军洼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片演怎。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡匕争,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出爷耀,到底是詐尸還是另有隱情甘桑,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布歹叮,位于F島的核電站跑杭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盗胀。R本人自食惡果不足惜艘蹋,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望票灰。 院中可真熱鬧女阀,春花似錦宅荤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庸汗,卻和暖如春惫确,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚯舱。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工改化, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枉昏。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓陈肛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兄裂。 傳聞我的和親對象是個殘疾皇子句旱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容