前面吐槽了一番 现在来记录下后端项目的搭建
当前 将写core项目的基本搭建,token鉴权的获取与使用
首先我们看下搭建的基本样子:

一、项目搭建
- 要搭建core 5 的项目需要将vs2019升级到最新。好像是升级到16版本就可以了
 - 启动vs2019 找到这个ASP.NET Core 空项目 这是空的,什么都需要自己建,也可以选择其他有默认文件的
 - 选择保存位置和设置项目名称就可以了
 - 上面完成后会到这个页面 默认是选择3.1版本的 可以选择修改掉
 上面是否启动Docker 看自己的需要,,之后点击创建就可以了 至此项目已经创建完成了 - 创建完成就是如下的样子了 一个空空如也的项目
 appsettings.json:以键值对的形式保存的配置文件 Program.cs:项目的启动入口 一般不做修改 Startup.cs:core项目的配置入口,不管是AOP、过滤器还是管道的注册与使用 都是在这里面做配置,也是中间件的入口
二、配置JWT权限认证
- 添加一个类库 注意是core的类库 目标框架也要选择是net5
然后创建一个JWT的文件夹,这个文件夹里面存放token的相关配置和验证
添加如下两个类库 
使用引入如下的nugget包:
Microsoft.IdentityModel.Tokens System.IdentityModel.Tokens.Jwt Microsoft.Extensions.Options Newtonsoft.Json Microsoft.AspNetCore.Mvc.Abstractions Microsoft.AspNetCore.Mvc.Formatters.Json
之后创建如下的文件: TokenType.cs: 存放token
public enum TokenType
{
Ok,
Fail,
Expired
}
public class TnToken
{
public string TokenStr { get; set; }
public DateTime Expires { get; set; }
}
BaseResultModel:统一的返回实体
public class BaseResultModel
{
public BaseResultModel(int? code = null, string message = null,
object result = null, ReturnStatus returnStatus = ReturnStatus.Success)
{
this.Code = code;
this.Data = result;
this.Msg = message;
this.ReturnStatus = returnStatus;
}
public int? Code { get; set; }
public string Msg { get; set; }
public object Data { get; set; }
public ReturnStatus ReturnStatus { get; set; }
public TnToken TnToken { get; set; }
}
public enum ReturnStatus
{
Success = 1,
Fail = 0,
ConfirmIsContinue = 2,
Error = -1
}
ErrCode:统一返回错误管理
public static class ErrCode
{
public static string err0 = "请求成功";
public static int successCode = 200;
public static int serverErrCode = 500;
public static string err1 = "请求失败";
public static int failCode = 201;
public static string err40001 = "获取access_token时AppID或AppSecret错误。请开发者认真比对appid和AppSecret的正确性,或查看是否正在为恰当的应用调用接口";
public static string err40002 = "调用接口的服务器URL地址不正确,请联系供应商进行授权";
public static string err40003 = "未获取到用户令牌token。请开发者验证token是否包含在请求报文的头部中 并且key值为:\"token\" ";
public static string err40004 = "不合法的凭证类型";
public static string err40005 = "用户令牌accesstoken超时失效";
public static string err40006 = "您未被授权使用该功能,请重新登录试试或联系系统管理员进行处理";
public static string err40007 = "传递参数出现错误";
public static string err40008 = "用户未登录或超时";
public static string err40009 = "token验证失败";
public static string err43001 = "新增数据失败";
public static string err43002 = "更新数据失败";
public static string err43003 = "删除数据失败";
public static string err500 = "程序异常";
public static string err50001 = "该用户不存在";
public static string err50002 = "用户已存在,请登录或重新注册!";
public static string err50003 = "会员注册失败";
public static string err60001 = "查询数据不存在";
}
JWTConfig.cs :配置token生成信息
public class JWTConfig
{
public string UID { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public string IssuerSigningKey { get; set; }
public int AccessTokenExpiresMinutes { get; set; }
}
ITokenHelper.cs: token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
public interface ITokenHelper
{
TnToken CreateToken<T>(T user) where T : class;
TnToken CreateToken(Dictionary<string, string> keyValuePairs);
bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null);
TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action);
}
然后实例上面的接口方法 TokenHelper.cs:
public class TokenHelper : ITokenHelper
{
private readonly IOptions<JWTConfig> _options;
public TokenHelper(IOptions<JWTConfig> options)
{
_options = options;
}
public TnToken CreateToken<T>(T user) where T : class
{
List<Claim> claims = new List<Claim>();
foreach (var item in user.GetType().GetProperties())
{
object obj = item.GetValue(user);
string value = "";
if (obj != null)
value = obj.ToString();
claims.Add(new Claim(item.Name, value));
}
return CreateToken(claims);
}
public TnToken CreateToken(Dictionary<string, string> keyValuePairs)
{
List<Claim> claims = new();
foreach (var item in keyValuePairs)
{
claims.Add(new Claim(item.Key, item.Value));
}
return CreateTokenString(claims);
}
private TnToken CreateTokenString(List<Claim> claims)
{
var now = DateTime.Now;
var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
var token = new JwtSecurityToken(
issuer: _options.Value.Issuer,
audience: _options.Value.Audience,
claims: claims,
notBefore: now,
expires: expires,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
return new TnToken { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
}
public bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null)
{
var success = true;
var jwtArr = encodeJwt.Split('.');
if (jwtArr.Length < 3)
{
return false;
}
_ = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
success = success && string.Equals(jwtArr[2],
Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
if (!success)
{
return success;
}
var now = ToUnixEpochDate(DateTime.UtcNow);
success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
if (validatePayLoad == null)
return true;
success = success && validatePayLoad(payLoad);
return success;
}
private long ToUnixEpochDate(DateTime date)
{
return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
}
public TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action)
{
if (string.IsNullOrEmpty(encodeJwt))
{
return TokenType.Fail;
}
var jwtArr = encodeJwt.Split('.');
if (jwtArr.Length < 3)
{
return TokenType.Fail;
}
_ = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))
{
return TokenType.Fail;
}
var now = ToUnixEpochDate(DateTime.UtcNow);
if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())))
{
return TokenType.Expired;
}
if (validatePayLoad == null)
{
action(payLoad);
return TokenType.Ok;
}
if (!validatePayLoad(payLoad))
{
return TokenType.Fail;
}
action(payLoad);
return TokenType.Ok;
}
}
然后添加相关的引入就可以 TokenFilter.cs:token的过滤器 验证权限就在这里做
public class TokenFilter : Attribute, IActionFilter
{
private readonly ITokenHelper tokenHelper;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOptions<JWTConfig> _options;
public TokenFilter(ITokenHelper _tokenHelper, IHttpContextAccessor httpContextAccessor, IOptions<JWTConfig> options)
{
tokenHelper = _tokenHelper;
_httpContextAccessor = httpContextAccessor;
_options = options;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
BaseResultModel ret = new();
try
{
object tokenobj = null;
var headers = _httpContextAccessor.HttpContext.Request.Headers;
tokenobj = headers["token"];
if (tokenobj == null || string.IsNullOrWhiteSpace(tokenobj.ToString()))
{
ret.Code = 40003;
ret.Msg = ErrCode.err40003;
context.Result = new JsonResult(ret);
return;
}
string token = tokenobj.ToString();
string userId = "";
TokenType tokenType = tokenHelper.ValiTokenState(token,
a => a["iss"] == _options.Value.Issuer && a["aud"] == _options.Value.Audience,
action => { userId = action[_options.Value.UID]; });
if (tokenType == TokenType.Fail)
{
ret.Code = 40009;
ret.Msg = ErrCode.err40009;
context.Result = new JsonResult(ret);
return;
}
if (tokenType == TokenType.Expired)
{
ret.Code = 40008;
ret.Msg = ErrCode.err40008;
context.Result = new JsonResult(ret);
}
if (!string.IsNullOrEmpty(userId))
{
}
}
catch (Exception ex)
{
ret.Code = ErrCode.serverErrCode;
ret.Msg = "token验证失败,异常:" + ex.Message;
context.Result = new JsonResult(ret);
}
}
}
在appsettings文件里面加入如下代码:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"JWTConfig": {
"UID": "UID",
"Issuer": "October",
"Audience": "OCT",
"IssuerSigningKey": "October&YL889455200IBook_API",
"AccessTokenExpiresMinutes": "180"
}
}
相关的设置做完之后 我们进行token的注入并使用 回到Startup文件中 在iBook-core中添加引用:
Microsoft.AspNetCore.Authentication.JwtBearer 添加AOP项目的引入
Startup 中的 ConfigureServices 添加如下代码:
namespace iBook_core
{
public class Startup
{
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
#region jwt配置
services.AddTransient<ITokenHelper, TokenHelper>();
services.Configure<JWTConfig>(Configuration.GetSection("JWTConfig"));
services.AddAuthentication(Options =>
{
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).
AddJwtBearer();
services.AddScoped<TokenFilter>();
#endregion
}
}
}
测试使用:
添加测试的控制器  添加两个接口:
namespace iBook_core.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ITokenHelper _tokenHelper;
private readonly BaseResultModel model;
public ValuesController(ITokenHelper tokenHelper)
{
_tokenHelper = tokenHelper;
model = new();
}
[HttpGet]
[Route("getToken")]
public IActionResult GetToken(string uid)
{
Dictionary<string, string> keyValuePairs = new()
{
{ "UID", uid }
};
var token = _tokenHelper.CreateToken(keyValuePairs);
model.Code = 200;
model.Msg = "获取token成功";
model.Data = token;
model.TnToken = token;
return Ok(model);
}
[ServiceFilter(typeof(TokenFilter))]
[Route("getToken")]
[HttpPost]
public IActionResult verifyToken()
{
return null;
}
}
}
添加 Swagger ,这里可以不添加,用接口处测试工具进行测试也是可以的 引入
Swashbuckle.AspNetCore
将 Startup 修改如下:
namespace iBook_core
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "iBook-core", Version = "v1" });
});
#region jwt配置
services.AddTransient<ITokenHelper, TokenHelper>();
services.Configure<JWTConfig>(Configuration.GetSection("JWTConfig"));
services.AddAuthentication(Options =>
{
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).
AddJwtBearer();
services.AddScoped<TokenFilter>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
#endregion
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "iBook-core v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
启动项目 如果swagger没正常跳转 那就在地址加上https://localhost:44351/swagger/index.html token获取:  token验证失败的情况: 
[ServiceFilter(typeof(TokenFilter))] // 在需要token验证的地方加上这个注解 也可以直接加在控制器上 那么那个控制器下所有的接口都将需要提供token才可以
这个jwt的使用是几年前在其他地方看到的,找不到原作者了 就没法加上转载链接。为了配合自己的项目 改了一些。 若有侵权 联系删除
|