Aspnet Mvc 前后端分离项目手记(二)关于token认证

在前后端分离的项目中,首先我们要解决的问题就是身份认证

以往的时候,我们使用cookie+session,或者只用cookie来保持会话。

一,先来复习一下cookie和session

首先我们来复习一下在aspnet中cookie和session的关系,做一个简单试验

这是一个普通的view没有任何处理

可以看到,没有任何东西(cookie),然后当我们写入一个session之后

会发现多了一个名为ASP.NET_SessionId的cookie。我们都知道在aspnet中,session是保存在服务器端的内存中的,而http协议是无状态的,那么他是怎么确定不同请求的session

没错,session是借助cookie来实现的:cookie中保存着 session的key,当我们清除掉浏览器缓存时,会发现session也找不到了,就是这个原因。

使用session来保持会话有几个很严重的缺点:1 session容易丢失;2无法支持分布式;3,cookie 对跨域的支持不好

所以就用到了我们今天说的token

二,token 

1,token的产生

一般是用户登录成功,服务器端产生一个token并返给前端,前端将token保存在cookie或者localStorage里面,然后每次请求时都带上这个token,一般都带在请求头里面

 2,token的内容

一般的token里面必须有的是:1,会话用户的标识:比如userid。2,token的过期时间,如果想更完整一点,可以加上token的颁发者,签名等等

3,token的生成算法,一般是由服务器端将token的主要内容,过期时间等等做非对称加密,然后进行签名算法(防止客户端更改),具体看后面jwt

4,token校验

当服务器端收到请求时,首先会校验token,校验有两种不同的方式

 一, token产生后保存在服务器端(redis或者其他比较速度快的缓存中) 。优点:可控性强,可以用这个来做单点登录,比如另一个地方登录,就remove掉之前的token。缺点:实现麻烦一点,而且要占服务器压力

二, token产生后服务器端不保存,只负责校验。 优点:大大降低了服务器的压力,实现起来,也要相对简单一点。缺点:token一旦颁发,服务器端就不可控了,只能等它过期。

    具体用哪种看具体的需求。如果不是做可控性要求很强,个人建议第二种。

5 jwt 

jwt 全名Json Web Tokens,算是一种token的规范吧

园子里面有很不不错的介绍 ,比如这篇:阮一峰 jwt介绍   http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

 

组成有三部分

  • Header(头部,一般包含了token的签名方式)
  • Payload(负载,也就是具体的有效部分)
  • Signature(签名,将前两部分进行签名算法,防止客户端篡改)

实现方式,将header部分和payload部分分别进行base64算法,然后用点号“.”隔开拼接,然后进行签名算法,然后在将三部分拼接(点号隔开)就得到了jwt

注意 ,jwt默认是采用base64编码的,也就是说 客户端也能解码得出具体内容的,所以除非特殊情况,重要敏感字段一定不能放在token中

以下是具体实现

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Security.Cryptography;
  5 using System.Text;
  6 using System.Web;
  7
  8 namespace Rk.JWT
  9 {
 10
 11     public class Jwt
 12     {
 13
 14         //参考自 阮一峰 jwt介绍  http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
 15
 16
 17
 18         public static string SALT = "OXpcRP8jmCfMKumY";
 19
 20
 21
 22         /// <summary>
 23         ///
 24         /// </summary>
 25         /// <param name="ExraPayload">额外的信息</param>
 26         /// <returns></returns>
 27         public static string Create(Dictionary<string,object> ExraPayload)
 28         {
 29             var Header = new Dictionary<string, string>();
 30             Header.Add("tp", "MD5");
 31             var Payload = new Dictionary<string, object>();
 32
 33             //JWT 规定了7个官方字段,供选用。
 34             Payload.Add("iss", "signBy"); //颁发人
 35             Payload.Add("jti", Guid.NewGuid().ToString()); //jwt的id
 36             Payload.Add("exp",System.DateTime.Now.AddMinutes(20));//过期时间
 37             Payload.Add("nbf", System.DateTime.Now);//生效时间
 38             Payload.Add("iat", System.DateTime.Now);//签发时间
 39             Payload.Add("sub", "subject");//主题
 40             Payload.Add("aud", "audience");//受众
 41
 42             foreach (var item in ExraPayload)
 43             {
 44                 if (Payload.ContainsKey(item.Key))
 45                 {
 46
 47                     throw new Exception($"{item.Key}键值已被占用 不能使用 ");
 48                 }
 49                 else
 50                 {
 51                     Payload.Add(item.Key, item.Value);
 52                 }
 53             }
 54             string base64Header = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Header));
 55             string base64Payload = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Payload));
 56             string tmp = base64Header + "." + base64Payload;
 57
 58             string sign = Md5(tmp+ SALT);//加盐,重要
 59
 60             return base64Header+"."+ base64Payload+"."+ sign;
 61         }
 62
 63
 64         //校验是否合法,是否过期
 65         public static bool Check(string token)
 66         {
 67             string base64Header = token.Split(‘.‘)[0];
 68             string base64Payload = token.Split(‘.‘)[1];
 69             string sign = token.Split(‘.‘)[2];
 70             string tmp = base64Header + "." + base64Payload;
 71             var signCheck = Md5(base64Header + "." + base64Payload + SALT);
 72             if(signCheck!= sign)
 73             {
 74                 return false;
 75             }
 76             var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
 77             if(  Convert.ToDateTime(dic["exp"])<System.DateTime.Now)
 78             {
 79                 //过期了
 80                 return false;
 81
 82             }
 83             return true;
 84
 85         }
 86         //校验是否合法,是否过期
 87         public static Dictionary<string,object> GetPayLoad(string token)
 88         {
 89
 90             string base64Payload = token.Split(‘.‘)[1];
 91
 92             var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
 93
 94             return dic;
 95
 96         }
 97
 98
 99         public static string Base64Url(string input)
100         {
101             //JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。
102             //Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。
103             string output = "";
104                 byte[] bytes = Encoding.UTF8.GetBytes(input);
105             try
106             {
107                 output = Convert.ToBase64String(bytes).Replace(‘+‘, ‘-‘).Replace(‘/‘, ‘_‘).TrimEnd(‘=‘) ;
108
109
110             }
111             catch (Exception e)
112             {
113                 throw e;
114             }
115             return output;
116         }
117         public static string Base64UrlDecode(string input)
118         {
119             string output = "";
120
121             input = input.Replace(‘-‘, ‘+‘).Replace(‘_‘, ‘/‘);
122             switch (input.Length % 4)
123             {
124                 case 2:
125                     input += "==";
126                     break;
127                 case 3:
128                     input += "=";
129                     break;
130             }
131             byte[] bytes = Convert.FromBase64String(input);
132             try
133             {
134                 output = Encoding.UTF8.GetString(bytes);
135             }
136             catch
137             {
138                 output = input;
139             }
140             return output;
141         }
142         public static string Md5(string input,int bit=16)
143         {
144
145             MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
146             byte[] hashedDataBytes;
147             hashedDataBytes = md5Hasher.ComputeHash(Encoding.GetEncoding("gb2312").GetBytes(input));
148             StringBuilder tmp = new StringBuilder();
149             foreach (byte i in hashedDataBytes)
150             {
151                 tmp.Append(i.ToString("x2"));
152             }
153             if (bit == 16)
154                 return tmp.ToString().Substring(8, 16);
155             else
156             if (bit == 32) return tmp.ToString();//默认情况
157             else return string.Empty;
158
159         }
160     }
161
162 }

使用方式

 1 public class HomeController : BaseController
 2    {
 3
 4
 5        public ActionResult Login(string username, string pwd)
 6        {
 7
 8            /// 1, todo 验证用户名密码正确
 9
10
11            //2,//在token中加入用户id,创建token
12            var dic = new Dictionary<string, object>();
13            dic.Add("userid", "20125521225858");
14            string token = JWT.Jwt.Create(dic);
15
16
17            //验证token是否正确是否过期
18            var isChecked = JWT.Jwt.Check(token);
19
20            return Content("");
21
22        }
23    }

下一篇我们将会聊一聊 rest 风格url在前后端分离项目中的使用

原文地址:https://www.cnblogs.com/wahson2019/p/10860973.html

时间: 2024-11-08 13:25:10

Aspnet Mvc 前后端分离项目手记(二)关于token认证的相关文章

Aspnet Mvc 前后端分离项目手记(一) 关于跨域问题(还有前言)

前言,最近的项目使用前后端分离的模式,记录其中一些知识点.经过这个项目,也对前后端分离有了更多理解,尤其是在技术之外的方面. 越来越多的项目采用前后端分离的原因,有两点:      1,技术方面的原因:移动端的越来越重要,一个项目可能要做好几种版本,手机端,app,pc...,,前端完全负责页面展示后端只负责统一的api: react,vue,ng一些优秀框架的产生,解决了很多前端开发者的痛点:还有一些随之而来的成熟框架elementui,iview等等.       2 ,非技术方面的原因 

Aspnet前后端分离项目手记(二)关于token认证

在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习一下在aspnet中cookie和session的关系,做一个简单试验 这是一个普通的view没有任何处理 可以看到,没有任何东西(cookie),然后当我们写入一个session之后 \ 会发现多了一个名为ASP.NET_SessionId的cookie.我们都知道在aspnet中,session

基于Vue的前后端分离项目实践

一.为什么需要前后端分离 1.1什么是前后端分离  前后端分离这个词刚在毕业(15年)那会就听说过,但是直到17年前都没有接触过前后端分离的项目.怎么理解前后端分离?直观的感觉就是前后端分开去做,即功能和职责上的一种划分,前端负责页面的渲染,部分页面交互的逻辑,然后通过网络请求与后端进行数据的交互:后端则着重关注业务逻辑的处理,直接操控数据库. 1.2前后端未分离前(1)jsp + servlet 开发模式: JSP页面:负责视图层的渲染及交互,内部可以嵌入java 代码,在某些场景下开发起来比

docker-compose 部署 Vue+SpringBoot 前后端分离项目

一.前言 本文将通过docker-compose来部署前端Vue项目到Nginx中,和运行后端SpringBoot项目 服务器基本环境: CentOS7.3 Dokcer MySQL 二.docker-compose 部署Vue+SpringBoot 前后端分离项目 整体项目配置结构,这里在不影响原来项目的结构,因此将所有配置文件都提出来存放到docker文件夹内了,但注意docker-compose文件须放到项目总的根目录下哦! 1.新增后端所需配置文件api-Dockerfile # 指定基

SpringSecurity实现前后端分离项目的登录认证

一.文章简介 本文简要介绍了spring security的基本原理和实现,并基于springboot整合了spring security实现了基于数据库管理的用户的登录和登出,登录过程实现了验证码的校验功能. 二.spring security框架简介 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.主要包括:用户认证(Authentication)和用户授权(Authorization)两个部分.用户认证指的是验证某个用户能

《Spring Boot 入门及前后端分离项目实践》系列介绍

课程计划 课程地址点这里 本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 个部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 项目实践开发.Spring Boot 介绍.前后端分离.API 规范等内容旨在让读者更加熟悉 SpringBoot 及企业开发中需要注意的事项并具有使用 SpringBoot 技术进行基本功能开发的能力:这最后的项目实战为课程的主要部分,我会带着大家实际的开发一个前后端分离的 Spring Boot 实践项目,

Docker环境下的前后端分离项目部署与运维(一)项目简介及环境要求

项目简介 本教程将从零开始部署一个前后端分离的开源项目,利用docker虚拟机的容器技术,采用分布式集群部署,将项目转换成为高性能.高负载.高可用的部署方案.包括了MySQL集群.Redis集群.负载均衡.双机热备等等. 部署图 所用到的主流技术 Docker容器.前后端集群.MySQL集群.Redis集群.Haproxy负载均衡.Nginx负载均衡.Keepalived实现双机热备 前后端分离项目部署图 前后端分离项目开源框架介绍 本次教程所采用的前后端分离的项目开源框架是人人网的renren

python 记录Django与Vue前后端分离项目搭建

python 记录Django与Vue前后端分离项目搭建 参考链接: https://blog.csdn.net/liuyukuan/article/details/70477095 1. 安装python与vue 2. 创建Django项目 django-admin startproject ulb_manager 3. 进入项目并创建名为backeng的app cd ulb_manager   python manage.py startapp backend 4. 使用vue-cli创建v

Docker环境下的前后端分离项目部署与运维

const 课程地址 = " http://icourse8.com/Dockerbushu.html "; 详细信息第1章 课程介绍 第2章 人人开源前后端分离项目下载与配置 第3章 Linux基础知识强化 第4章 搭建MySQL集群 第5章 搭建Redis集群 第6章 部署前后端分离项目 第7章 课后作业 第8章 云平台部署前后端分离 第9章 课程总结 var countAndSay = function(n) { return createStr(1, '1', n) funct