草庐IT

.net6 Web Api使用JWT-从后端到前端全部过程

故里2130 2023-04-22 原文

jwt是做验证的必经之路,至于原理,就不在叙述了,可以参考官网

jwt官网介绍

JSON Web Tokens - jwt.io

原理介绍

JSON Web Token 入门教程 - 阮一峰的网络日志

看完之后,结合这个图,就明白了。

本案例使用vs2022,.net6api做后端,以及vue3做前端来完成功能。

1.创建一个可执行的.net6api后端 

2.安装jwt,要注意版本 

3. 在appsettings.json中添加JWT加密需要的私钥AuthenticationDemo

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AuthenticationDemo": { //这些键值对都可以自定义
    "SecretKeyDemo": "aaaaaaaaaadddddddddddddffffffffffffwwwwwwww", //私钥
    "IssuerDemo": "guli2130", //发布者
    "AudienceDemo": "audience" //接收者
  }

}

4.新建一个GetToken类,再建立一个LoginToken方法

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace net6ApiJWT.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class GetToken : ControllerBase
    {
        public readonly IConfiguration configuration;
        public GetToken(IConfiguration configuration)         //注入配置,用来获取appsettings.json的配置
        {
            this.configuration = configuration;
        }
        [HttpGet]
        public ActionResult<string> LoginToken()
        {

            //1.验证用户账号密码是否正确,暂时忽略,因为我们是模拟登录

            //2.生成JWT
            //Header,选择签名算法
            var signingAlogorithm = SecurityAlgorithms.HmacSha256;
            //Payload,存放用户信息,下面我们放了一个用户id
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub,"userId")
            };
            //Signature
            //取出私钥并以utf8编码字节输出
            var secretByte = Encoding.UTF8.GetBytes(configuration["AuthenticationDemo:SecretKeyDemo"]);
            //使用非对称算法对私钥进行加密
            var signingKey = new SymmetricSecurityKey(secretByte);
            //使用HmacSha256来验证加密后的私钥生成数字签名
            var signingCredentials = new SigningCredentials(signingKey, signingAlogorithm);
            //生成Token
            var Token = new JwtSecurityToken(
                    issuer: configuration["AuthenticationDemo:IssuerDemo"],        //发布者
                    audience: configuration["AuthenticationDemo:AudienceDemo"],    //接收者
                    claims: claims,                                         //存放的用户信息
                    notBefore: DateTime.UtcNow,                             //发布时间
                    expires: DateTime.UtcNow.AddDays(1),                      //有效期设置为1天
                    signingCredentials                                      //数字签名
                );
            //生成字符串token
            var TokenStr = new JwtSecurityTokenHandler().WriteToken(Token);

            return Ok(TokenStr);
        }
    }
}

5.运行api,可以看到生成的token

当我们把字符串复制到JWT官网,就可以是明文的,所以千万不要写账号和密码,如果要写,就再加密一层。 

6.在Program.cs中注入JWT身份认证服务

 里面有注释,加有跨域的问题,可以分开看。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        //取出私钥
        var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["AuthenticationDemo:SecretKeyDemo"]);
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            //验证发布者
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["AuthenticationDemo:IssuerDemo"],
            //验证接收者
            ValidateAudience = true,
            ValidAudience = builder.Configuration["AuthenticationDemo:AudienceDemo"],
            //验证是否过期
            ValidateLifetime = true,
            //验证私钥
            IssuerSigningKey = new SymmetricSecurityKey(secretByte)
        };
    });

//配置跨域服务
builder.Services.AddCors(options =>
{
    options.AddPolicy("cors", p =>
    {
        p.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader();

    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseSwagger();
app.UseSwaggerUI();

app.UseCors("cors");      //跨域
app.UseHttpsRedirection();

app.UseAuthentication();        //添加jwt验证  这2句千万不能忘记了,顺序不能颠倒。
//代码从上到下执行,中间可以加判断, 权限设置问题
app.UseAuthorization();

app.MapControllers();

app.Run();

至此,获取Token的任务就完成了。 

7.关键时刻,此时建立一个User类,再建立一个Login方法增加,并且增加[Authorize]。

如果在路由上面加[Authorize],那么应用于整个类的方法。

如果在HttpGet上面加[Authorize],那么应用这个方法。

无论增加哪里, 都不能访问Login了,都是401。

不加[Authorize],就是200

8.此时,使用Postman进行测试

依然是401

9.现在执行LoginToken方法,获取Token的值

10.把Token的值放在Authorization中

点击执行,就可以看到数据了。

也可以把Token的值放在Headers中,增加Authorization,还需要加bearer,后面加空格 。二选一即可。

11. 给api的方法增加权限

首先在Claim这里,赋值给登录用户一个admin的用户权限,也可以使用策略。

然后在具体的方法上面,增加角色,也就是只有admin才能访问这个方法,其他用户不能访问,就是200,其他用户访问就是401,当然这是在postman里面操作的。

12.其实不用postman, 在AddSwaggerGen方法中,可以配置输入token的小锁子

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddSwaggerGen();
// Register the Swagger generator, defining 1 or more Swagger documents
builder.Services.AddSwaggerGen(c =>
{


    //注册到swagger中
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "Value: Bearer {token}",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement()
        {{
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header }, new List<string>()
            }
        });
});



builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        //取出私钥
        var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["AuthenticationDemo:SecretKeyDemo"]);
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            //验证发布者
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["AuthenticationDemo:IssuerDemo"],
            //验证接收者
            ValidateAudience = true,
            ValidAudience = builder.Configuration["AuthenticationDemo:AudienceDemo"],
            //ValidateIssuerSigningKey= true,//是否验证SigningKey
            //验证是否过期
            ValidateLifetime = true,
            //验证私钥
            IssuerSigningKey = new SymmetricSecurityKey(secretByte)


        };
    });

//配置跨域服务
builder.Services.AddCors(options =>
{
    options.AddPolicy("cors", p =>
    {
        p.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader();

    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseSwagger();
app.UseSwaggerUI();

app.UseCors("cors");      //跨域
app.UseHttpsRedirection();

app.UseAuthentication();        //添加jwt验证  这2句千万不能忘记了,顺序不能颠倒。
//代码从上到下执行,中间可以加判断, 权限设置问题
app.UseAuthorization();

app.MapControllers();

app.Run();

13.然后启动后,就出现了,小锁子

14.此时,先点击LoginToken获取Token

再把Token输入里面去,注意加上Bearer和后面的空格,再执行Login方法就成功了。

此时,我们就可以脱离postman了。

15.后端已经全部完成了,现在做前端

前端使用vue3,只说配置即可,具体创建项目不说了,我之前也写了。

我们在axios中配置headers的Authorization。实际项目肯定不能直接写死

import axios from 'axios'
import {
	ElLoading
} from 'element-plus'



//export将service传出去
export const service = axios.create({
	baseURL: 'https://localhost:7193/api',
	headers:{
		'Authorization':'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VySWQiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1pbiIsIm5iZiI6MTY3MzYwMDY2MiwiZXhwIjoxNjczNjg3MDYyLCJpc3MiOiJndWxpMjEzMCIsImF1ZCI6ImF1ZGllbmNlIn0.2_EQjQzi8ZmaPoTR4hdBrePiijkk9J25nZS0KIBx6OA'
	}
})


 

 

实际项目中:

肯定在登录的时候,输入账号和密码,然后访问token的接口,这个接口是不加验证的,然后把得到的token保存到localStorage中。

	const Login = async () => {

		await service.get(`/GetToken/LoginToken`).then(res => {
			if (res.status == 200) {
				console.log(1111)
				//console.log(res.data)
				localStorage.setItem('token', res.data);
				console.log(222)
			}
		})

然后在axios中进行判断。  

import axios from 'axios'



//export将service传出去
export const service = axios.create({
	baseURL: 'http://localhost:5000/api'

})


//下面有2种写法,一种是声明函数的写法,一种是箭头函数的写法,都可以

//request interceptor  请求拦截器
service.interceptors.request.use(
	function(config) {
		// 在发送请求之前做些什么  Bearer
		//这里是流程,如果请求的是LoginToken接口,就放过,如果不是,那么就提示没有权限
		if (localStorage.getItem('token')) {
			config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`
		} else {
			if (!config.url.includes("LoginToken")) {
				alert("没有权限")
			}
		}
		console.log('这里是请求前')
		//这里是使用了element-plus,进行模态化窗体,也就是等待查询的意思,本案例在api中,设置了等待时间
		return config
	},
	function(error) {
		// 对请求错误做些什么

		console.log(error)
		console.log('这里是请求错误')

		return Promise.reject(error)
	}
)


//响应拦截器
service.interceptors.response.use(
	res => {
		// 在请求成功后的数据处理
		if (res.status === 200) {
			console.log(res.status)
			console.log('这里是请求成功后')


			return res;
		} else {
			console.log(res.status)
			console.log('这里是请求失败后')


			return res;
		}

	},
	err => {
		// 在响应错误的时候的逻辑处理
		console.log('这里是响应错误')

		return Promise.reject(err)
	});

16.在界面调用

await service.get(`https://localhost:7193/api/User/Login?userId=1&pwd=1`).then(res => {
			if (res.status == 200) {
				console.log(1111)
				console.log(res.data)
				console.log(222)
			}
		})

17.效果 

源码:

https://download.csdn.net/download/u012563853/87383701

 参考:

这个是简单的认证。

.NET Core登录api时使用Basic认证_故里2130的博客-CSDN博客

有关.net6 Web Api使用JWT-从后端到前端全部过程的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐