如今不少基於restful的api接口都有個登陸的設計,也就是在發起正式的請求以前先經過一個登陸的請求接口,申請一個叫作token的東西。申請成功後,後面其餘的支付請求都要帶上這個token,服務端經過這個token驗證請求的合法性。這個token一般都有一個有效期,通常就是幾個小時。java
好比我以前接入過一個支付寶和微信支付的通道,他們提供的api就要求先登陸獲取token而後才能使用支付的api接口。mysql
在好比微信的公衆平臺接口,關鍵的接口在使用以前都要帶access token。access_token是公衆號的全局惟一票據,有效期爲7200秒,重複獲取將致使上次獲取的access_token失效。web
接口調用請求說明redis
http請求方式: GETspring
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數說明sql
參數 是否必須 說明
grant_type 是 獲取access_token填寫client_credential
appid 是 第三方用戶惟一憑證
secret 是 第三方用戶惟一憑證密鑰,既appsecret
返回說明數據庫
正常狀況下,微信會返回下述JSON數據包給公衆號:json
{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數 說明
access_token 獲取到的憑證
expires_in 憑證有效時間,單位:秒
錯誤時微信會返回錯誤碼等信息,JSON數據包示例以下(該示例爲AppID無效錯誤):api
{"errcode":40013,"errmsg":"invalid appid"}
首先說它是一種規範。目的是在客戶端和服務端之間定義一種鑑權行爲從而保證數據傳遞的安全性。瀏覽器
JWT 標準的 Token 有三個部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
關於三個部分具體都是些什麼東西,你們自行搜索便可,不是我這篇文章的重點。
咱們說restful API中使用的token鑑權機制,大部分都是遵循JWT規範的,也就是底層都是對JWT的具體實現。
說了這麼多,該上實例了。這個實例會用到redis,spring等技術。
這個實例原出處:
RESTful登陸設計(基於Spring及Redis的Token鑑權)
原項目是基於maven的。我這裏爲了調試方便把項目遷移到myeclipse中,關於如何導入到myeclipse中,請參考:
上面的連接中對代碼解釋的也比較清楚了。我這裏只說下如何運行測試效果。
這個工程實際上是個基於spring boot的項目,Spring boot 的默認配置文件是 resources 下的 application.properties。因此咱們主要是修改它。
其中spring.datasource.*的配置項是mysql相關的,這裏解釋下。有人可能有疑問爲何程序裏並無看到引用這些配置變量。這是由於spring boot是一種約定優於配置的開發框架。好比,
spring.datasource.username就是表示數據庫用戶名,你不能隨便改,框架裏就用它做爲查抄依據。
同理spring.redis.*也是redis相關的配置。
上面幾個地方要根據你本地實際狀況修改。修改完以後,在你的mysql中新建一個名爲demo的數據庫(若是原來沒有的話)。而後執行工程中init.sql中的語句,這是爲了初始化表。
首先執行右鍵項目目錄,run as -> maven install,會看到相似下面的輸出:
而後再執行 run as -> java application, 而後選擇程序的入口:
一樣看下console有沒有錯誤,若是報錯一般是上面的配置不對,仔細檢查下。
打開瀏覽器,輸入http://localhost:8080,顯示以下:
代表運行成功。
測試下登陸,
登陸成功,而且也成功的建立了token。
退出登陸,在authorization中填寫用userId和token以」_」拼接獲得的字符串。
有人會問爲何authorization須要這樣的格式才能退出登陸成功。下面的代碼能夠說明問題,
...
@RequestMapping(method = RequestMethod.DELETE) @Authorization public ResponseEntity logout(@CurrentUser User user) { tokenManager.deleteToken(user.getId()); return new ResponseEntity<>(ResultModel.ok(), HttpStatus.OK); }
@Authorization用於表示該操做須要登陸後才能進行,不然會返回401錯誤。以下:
...
//驗證token TokenModel model = manager.getToken(authorization); if (manager.checkToken(model)) { //若是token驗證成功,將token對應的用戶id存在request中,便於以後注入 request.setAttribute(Constants.CURRENT_USER_ID, model.getUserId()); return true; } //若是驗證token失敗,而且方法註明了Authorization,返回401錯誤 if (method.getAnnotation(Authorization.class) != null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; }
爲了驗證redis的緩存有效期,我把代碼作了一點修改,過時時間設置成1分鐘便於測試。
/** * token有效期(分鐘) */ public static final int TOKEN_EXPIRES_MINS = 1; public TokenModel createToken(long userId) { //使用uuid做爲源token String token = UUID.randomUUID().toString().replace("-", ""); TokenModel model = new TokenModel(userId, token); //存儲到redis並設置過時時間 // redis.boundValueOps(userId).set(token, Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); redis.boundValueOps(userId).set(token, Constants.TOKEN_EXPIRES_MINS, TimeUnit.MINUTES); return model; }
而後我先登陸,等超過一分鐘再退出登陸,確認會返回401錯誤。