Live2D

vue+kotlin 实现基于JWT的 token认证

1.JWT

JWT全称(JsonWebToken),简单来说它是Json格式的加密字符串,其中包含敏感信息,使我们能够验证不同服务之间的发送者。

为什么要使用jwt呢?

在以往的验证用户状态,我们采取的是Cookie、Session机制。客户端将Cookie保存起来,服务端保存Session。在客户端请求服务端的时候会在请求头上携带Cookie信息,服务端会使用过滤器过滤验证Cookie信息,从而达到识别用户状态的效果。

Session和Cookie机制存在以下问题

  • Session是存在服务端的,需要占用服务器内存。如果是分布式的情况下,还要考虑Session共享问题
  • Cookie被截获,容易被伪造请求攻击
Token

基于以上问题,Token就出现了

Token是由在客户端第一次发来登录请求,服务端会生成一个token返回给客户端,由客户端自己存储。在每次请求资源时候需要带上token。服务端对token进行解密验证,验证通过则返回数据。

Token的流程如下图

image

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']
posted @ 2022-03-15 11:28  没有梦想的java菜鸟  阅读(404)  评论(0编辑  收藏  举报