前后端分离的登录

          

目录

          

        一、跨域认证的问题

​编辑 

        1.2 什么是JWT

        1.3 JWT原理

        1.4 JWT结构

        1.4.1 Header

        1.4.2  Payload

        1.4.3  Signature

        1.5 使用JWT---JAVA

               1.5.1 引入依赖

                1.5.2 封装一个JWT工具类  

        二、完成前后端分离的登录功能

        2.1 前端代码

        2.2 配置拦截器axios

        2.3 后台拦截器


        一、跨域认证的问题

        互联网服务离不开用户认证。一般流程是下面这样。

        

        1、用户向服务器发送用户名和密码。

        2、服务器验证通过后,在当前会话(session)里面保存相关数据,比如用户角色、登录时间等等。

        3、服务器向用户返回一个 session_id,写入用户的 Cookie。

        4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

        5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

 

        这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

 

        1.2 什么是JWT

        JSON Web Token是目前最流行的跨域认证解决方案,,适合前后端分离项目通过Restful API进行数据交互时进行身份认证. --JWT可以帮你生成一串字符串令牌。

        1.3 JWT原理

        JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

        {
          "姓名": "张三",
          "角色": "管理员",
          "到期时间": "2021年7月1日0点0分"
     }

        

        以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名.

        服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

        1.4 JWT结构

                实际的 JWT 大概就像下面这样。

 

        它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

        

        JWT 的三个部分依次如下。

        1.Header(头部) 数据格式是固定
        2.Payload(载荷) 包含用户信息以及过期时间等---信息
        3.Signature(签名)加密

        写成一行,就是下面的样子。

        Header.Payload.Signature

 

        下面依次介绍这三个部分。  

        1.4.1 Header

        Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

        

{
  "alg": "HS256",
  "typ": "JWT"
}

        上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

        最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

        1.4.2  Payload

                Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

        - iss (issuer):签发人
        - exp (expiration time):过期时间
        - sub (subject):主题
        - aud (audience):受众
        - nbf (Not Before):生效时间
        - iat (Issued At):签发时间
        - jti (JWT ID):编号

                除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。  

        

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

        注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

        这个 JSON 对象也要使用 Base64URL 算法转成字符串。

        1.4.3  Signature

        Signature 部分是对前两部分的签名,防止数据篡改

        首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

        

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

        算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

        1.5 使用JWT---JAVA

               1.5.1 引入依赖


<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>

                1.5.2 封装一个JWT工具类  

package com.aaa.qy158springboot02.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class JwtUtil {
    private static final String SIGN="aaa"; //密钥
    //1.通过jwt生成token字符串。
    public static String createToken(Map<String,Object> params){

        Map<String,Object> head=new HashMap<>();
        head.put("alg","HS256");
        head.put("typ","JWT");

        //定义颁发时间
        Date iss=new Date();
        //过期时间
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.MINUTE,30);
        Date expire = nowTime.getTime();

        String token = JWT.create()
                //指定头信息
                .withHeader(head)
                //载荷种的过期时间
                .withExpiresAt(expire)
                //颁发时间
                .withIssuedAt(iss)
                //颁发人
                .withIssuer("yankeqi")
                //自定义的载荷内容
                .withClaim("userInfo",params)
                //签名
                .sign(Algorithm.HMAC256(SIGN));

        return token;
    }

    //2. 判断token是否合法
    public static boolean verifyToken(String token){
        JWTVerifier build = JWT.require(Algorithm.HMAC256(SIGN)).build();
        try {
            DecodedJWT verify = build.verify(token);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    //3. 解析token种的内容
    public static Map<String,Object> decodeJWT(String token){
        Map<String, Object> userInfo = JWT.decode(token).getClaim("userInfo").asMap();
        return userInfo;
    }


    public static void main(String[] args) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","汪鹏");
        map.put("id",1);
        map.put("age",19);
        String token = createToken(map);
        System.out.println(token);

        Map<String, Object> map2
                = decodeJWT(token);
        System.out.println(map2);

//       boolean is=verifyToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySW5mbmPIiwiaWQiOjEsImFnZSI6MTl9LCJpc3MiOiJ5YW5rZXFpIiwiZXhwIjoxNjY5MTcwODA3LCJpYXQiOjE2NjkxNjkwMDd9.3BZh1ylEosSBfn2n3Y6xMKv8Ut9pyxm37Y0-JinJdB0");
//        System.out.println(is);
    }
}

        二、完成前后端分离的登录功能

        

        2.1 前端代码

<template>
    <div id="login">
        <div id="loginBox">
            <el-form :model="loginForm" status-icon :rules="loginRules" ref="ruleForm" label-width="100px" class="demo-ruleForm">

                <el-form-item label="账号" prop="name">
                    <el-input type="text" v-model="loginForm.name" autocomplete="off"></el-input>
                </el-form-item>

                <el-form-item label="密码" prop="age">
                    <el-input type="text" v-model="loginForm.age" autocomplete="off"></el-input>
                </el-form-item>

                <el-form-item>
                    <el-button type="primary" @click="login()">提交</el-button>
                    <el-button>重置</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Login.vue",
        data(){
            return{
                loginForm:{name:"",age:""},
                //校验
                loginRules:{
                    name:[{ required: true, message: '请输入账号', trigger: 'blur' },],
                    age:[{ required: true, message: '请输入密码', trigger: 'blur' },]
                }
            }
        },
        methods:{
            login(){
                this.$http.post("http://localhost:8000/login",this.loginForm).then(resp=>{
                    console.log(resp.data.code)
                    if (resp.data.code===2000){

                        //登录成功 获取服务器相应的token值,保存到localStorage
                        localStorage.setItem("token",resp.data.data);
                        //路由跳转  不能使用  location.href
                        this.$router.push("/student")
                    }else {
                        this.$message.error("登陆失败,账号密码错误");
                    }
                })
            }
        }
    }
</script>

<style>

    #loginBox{
        width: 500px;
        height: 290PX;
        margin: 100px auto;
    }
    #loginForm{
        margin:50px auto;

    }
</style>

        2.2 配置拦截器axios

//设置axios请求的拦截器
axios.interceptors.request.use(config=>{
  //从localStorage获取令牌信息
  var token = localStorage.getItem("token");
  //判断是否获取token 只要token有值 则返回true
  if (token){
    //把token信息放入请求头中
    config.headers.token=token;
  }
  return config;
})

        2.3 后台拦截器

package com.aaa.qy158springboot02.interceptor;

import com.aaa.qy158springboot02.util.JwtUtil;
import com.aaa.qy158springboot02.vo.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;


public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setContentType("application/json;charset=utf-8");
         //1.获取请求头种的token
        String token = request.getHeader("token");
        //2.判断token是否为null
        if(StringUtils.hasText(token)){
             //校验token是否合法
             if(JwtUtil.verifyToken(token)){
                  return true;
             }
        }
        PrintWriter writer = response.getWriter();
        Result result=new Result(4001,"未登录");
        String jsonString = JSON.toJSONString(result);
        writer.print(jsonString);
        writer.flush();
        writer.close();
        return false;
    }
}
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值