vue+kotlin 實現基於JWT的 token認證
阿新 • • 發佈:2022-03-15
1.JWT
JWT全稱(JsonWebToken),簡單來說它是Json格式的加密字串,其中包含敏感資訊,使我們能夠驗證不同服務之間的傳送者。
為什麼要使用jwt呢?
在以往的驗證使用者狀態,我們採取的是Cookie、Session機制。客戶端將Cookie儲存起來,服務端儲存Session。在客戶端請求服務端的時候會在請求頭上攜帶Cookie資訊,服務端會使用過濾器過濾驗證Cookie資訊,從而達到識別使用者狀態的效果。
Session和Cookie機制存在以下問題
- Session是存在服務端的,需要佔用伺服器記憶體。如果是分散式的情況下,還要考慮Session共享問題
- Cookie被截獲,容易被偽造請求攻擊
Token
基於以上問題,Token就出現了
Token是由在客戶端第一次發來登入請求,服務端會生成一個token返回給客戶端,由客戶端自己儲存。在每次請求資源時候需要帶上token。服務端對token進行解密驗證,驗證通過則返回資料。
Token的流程如下圖
JWT的構成
header
jwt的頭部有兩部分組成
- 宣告型別
- 宣告加密的演算法
{
'typ': 'JWT',
'alg': 'HS256'
}
Payload
載荷就是存放有效資訊的地方
signature
簽名信息
2.程式碼實現
kotlin服務端
登入介面
object LoginController { suspend fun loginAction(call: ApplicationCall) { val result = ResponseResult() if (call.parameters["username"]=="zs"&&call.parameters["password"]=="1234"){ result.code=200 result.message="登陸成功" call.response.header("Access-Control-Expose-Headers","Authorization") call.response.header("Authorization",JwtUtils.generationToken(call.parameters["username"]!!)) }else{ result.code=403 result.message="使用者名稱或者密碼錯誤" } call.respond(result) } }
攔截器
import io.ktor.routing.* class TokenRouteSelector(val role: String) : RouteSelector(RouteSelectorEvaluation.qualityConstant) { override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation { return RouteSelectorEvaluation.Constant } override fun toString(): String = "(tokenCheck ${role})" }
fun Route.tokenCheck(role: String = "default", build: Route.() -> Unit): Route {
val route = createChild(TokenRouteSelector(role))
route.intercept(ApplicationCallPipeline.Features) {
val token = call.request.headers["Authorization"]
println(token)
if (call.request.uri.contains("login")) {
proceed()
} else {
token?.let {
if (JwtUtils.getClaimByToken(token).toString() != "{}") {
if (!JwtUtils.isTokenExpired(JwtUtils.getClaimByToken(token))) {
proceed()
}
} else {
call.respond(HttpStatusCode.Forbidden, mapOf("code" to "error token"))
finish()
}
}?: let {
call.respond(HttpStatusCode.Unauthorized, mapOf("code" to "no auth token"))
finish()
}
}
}
route.build()
return route
}
jwt工具類
package com.oasis.tga.utils
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.impl.DefaultClaims
import java.util.*
class JwtUtils {
companion object{
private val nowDate = Date()
private val expireDate = Date(nowDate.time + 1000 * 60 * 60 * 24 * 7)
private val secret = "dwadadag234423"
// 生成token
fun generationToken(username: String): String {
return Jwts.builder()
// header
.setHeaderParam("typ", "JWT")
// payload
.claim("username", username)
.setSubject(username)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.setId(UUID.randomUUID().toString())
// signature
.signWith(SignatureAlgorithm.HS256, secret)
.compact()
}
// 解析token
fun getClaimByToken(jwt: String): Claims {
return try {
Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(jwt)
.body
} catch (e: Exception) {
DefaultClaims()!!
}
}
// 判斷按是否過期
fun isTokenExpired(claims: Claims):Boolean{
return claims.expiration.before(Date())
}
}
}
vue客戶端
登入請求 login.vue
const that = this
this.$refs[formName].validate((valid) => {
if (valid) {
this.axios.get('http://localhost:8080/api/login', {params: this.form}).then(function (response) {
if (response.data.code == 200 && response.data.message === "登陸成功") {
console.log(response)
//全域性儲存token
window.localStorage["Authorization"] =response.headers['authorization'];
// 使用 vue-router 路由到指定頁面,該方式稱之為程式設計式導航
// that.$router.push("/main");
} else {
that.$message.error('使用者名稱或密碼錯誤');
}
})
} else {
this.dialogVisible = true;
return false;
}
});
index.js
// 導航守衛
// 使用 router.beforeEach 註冊一個全域性前置守衛,判斷使用者是否登陸
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else {
let token = localStorage.getItem('Authorization');
if (token === null || token === '') {
next('/login');
} else {
next();
}
}
});
main.js
// 攔截請求
axios.interceptors.request.use(
config => {
config.headers.Authorization = window.localStorage["Authorization"]
return config
}
);
//攔截響應
axios.interceptors.response.use(
res => {
return res
},
error => {
if (error.response.status === 403) {
window.localStorage.removeItem('Authorization');
router.push({name: 'login'})
}
}
);
App.vue 引入表單重新整理-跟token程式碼無關
export default {
name: 'App',
provide(){
return {
reload: this.reload
}
},
data(){
return {
isRouterAlive:true
}
},
methods:{
reload(){
this.isRouterAlive=false
this.$nextTick(()=>this.isRouterAlive=true)
}
},
}
使用以下程式碼引入
inject: ['reload']