# 微服务学习_C#进阶之学习设计微服务:api认证

# 微服务学习_C#进阶之学习设计微服务:api认证_第1张图片

前言
最近在学习微服务,所以把自己的个人站点https://www.ttblog.site/拆分成微服务。目前正在思考微服务里面的认证与授权,网上百度到都是根据用户名和密码来实现的,考虑到实际的原因,我的个人站点是最先访问不需要登录,当执行写入或更改操作时才需要用户名和密码,所以我自己思考了一个方案,这里分享一下,设计难免有很多不合理之处,大家可以予以批评。文档
我开始做的时候,对认证授权不是很理解,所以我在网上百度并且在博客园和开源中国提了一下问。https://www.oschina.net/question/2859520_2319077和https://q.cnblogs.com/q/129422/。并且看了很多文章,大多数使用的是IdentityServer4,但是我发现这个比较复杂,貌似还要安装一些认证,所以选择了使用JWT。并且了解了一下OAuth2,我觉得我用的应该属于里面的客户端模式https://www.jianshu.com/p/84a4b4a1e833。大概都了解之后,我就开始在项目里集成了jwt和ocelot。实战
首先创建了一个认证服务器
BlogAuthApi
然后一个网关
BlogGateway
最后一个
BlogWebApi
我的思路就是js判断是否存有token,如果没有在请求认证服务器Auth,,返回一个token,存入浏览器,然后之后通过token去访问webapi。1,请求token
我这里使用的微服务网关属于Ocelot,请求时通过网关转发到认证服务器获取token,如下代码生成token:

public class Jwt

{

///

/// 返回jwt模型

///

///

public static JwtOption GetOption()

{

JwtOption option = ConfigureProvider.BuildModel("jwtOption");

return option;

}

///

/// 返回SymmetricSecurityKey

///

///

public static SymmetricSecurityKey GetSymmetricSecurityKey()

{

JwtOption option = GetOption();

return GetSymmetricSecurityKey(option.Secret);

}

///

/// 返回SymmetricSecurityKey

///

///

public static SymmetricSecurityKey GetSymmetricSecurityKey(string secret)

{

return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));

}

///

/// 返回token参数模型

///

///

public static TokenValidationParameters GetTokenValidation()

{

JwtOption option = GetOption();

var tokenValidationParameters = new TokenValidationParameters

{

ValidateIssuerSigningKey = true,

IssuerSigningKey = GetSymmetricSecurityKey(option.Secret),

ValidateIssuer = true,

ValidIssuer = option.Issuer,

ValidateAudience = true,

ValidAudience = option.Audience,

ValidateLifetime = true,

ClockSkew = TimeSpan.Zero,

RequireExpirationTime = true,

};

return tokenValidationParameters;

}

///

/// 获取jwt的token参数

///

///

///

public static JwtSecurityToken GetJwtParameters(Claim[] claims,JwtOption option=null)

{

if (option == null)

option = GetOption();

var jwt = new JwtSecurityToken(

issuer: option.Issuer,

audience: option.Audience,

claims: claims,

notBefore: DateTime.Now,

expires: DateTime.Now.Add(TimeSpan.FromMinutes(option.ExpireMinutes)),

signingCredentials: new SigningCredentials(GetSymmetricSecurityKey(option.Secret), SecurityAlgorithms.HmacSha256)

);

return jwt;

}

///

/// 获取jwt

///

///

///

public static JwtToken GetToken(JwtSecurityToken tokenParameters)

{

JwtOption option = GetOption();

string token=new JwtSecurityTokenHandler().WriteToken(tokenParameters);

return new JwtToken(token, option.ExpireMinutes);

}

///

/// 获取jwt

///

///

///

public static JwtToken GetToken(Claim[] claims)

{

JwtOption option = GetOption();

JwtSecurityToken jwtSecurityToken = GetJwtParameters(claims,option);

string token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

return new JwtToken(token, option.ExpireMinutes*60);

}

}

public class JwtToken

{

public string Token { get; set; }

public int ExpireSeconds { get; set; }

public JwtToken(string token,int expireSeconds)

{

Token = token;

ExpireSeconds = expireSeconds;

}

}

public class JwtOption

{

public string Issuer { get; set; }

public string Audience { get; set; }

public int ExpireMinutes { get; set; }

public string Secret { get; set; }

}

 
49354cf8d77af969c0c368ff0d6f96c6.gif

并且添加配置文件

 "jwtOption": {    "issuer": "",    "audience": "",    "expireMinutes": "",    "secret": ""  }
2482466d0fd2cc1235be4217e11683cc.gif

然后前端获取到token之后会把token放入到header里面请求。2,配置网关服务Ocelot使用ocelot认证时,需要配置Ocelot.json,对相应的路由添加节点

{

"DownstreamPathTemplate": "/api/{url}",

"DownstreamScheme": "http",

"DownstreamHostAndPorts": [

{

"Host": "localhost",

"Port": 5001

}

],

"UpstreamPathTemplate": "/{url}",

"UpstreamHttpMethod": [ "Get", "Post", "Delete" ],

"AuthenticationOptions": {

"AuthenticationProviderKey": "ApiAuthKey",//认证服务的key

"AllowedScopes": []

},

//限流

"RateLimitOptions": {

"ClientWhitelist": [],

"EnableRateLimiting": true,

"Period": "1s",

"PeriodTimespan": 1,

"Limit": 1

}

}

2482466d0fd2cc1235be4217e11683cc.gif

然后需要在Startup里面添加Jwt如下:

services.AddAuthentication()

.AddJwtBearer("ApiAuthKey", x =>

{

x.RequireHttpsMetadata = false;

x.TokenValidationParameters = tokenValidationParameters;

});

2482466d0fd2cc1235be4217e11683cc.gif

之后启动3个服务来测试下,
当我们不传token时,请求时直接返回401的:

# 微服务学习_C#进阶之学习设计微服务:api认证_第2张图片


然后我们请求认证服务器获取token

5b4277b69133f38af33c4889d008f193.png


然后我们把token放入header里面请求:

# 微服务学习_C#进阶之学习设计微服务:api认证_第3张图片


可以看到请求成功了,并且我们可以看到token的过期时间为120秒,然后过了两分钟我们再请求就不行了

# 微服务学习_C#进阶之学习设计微服务:api认证_第4张图片

到此,我的api认证功能已经大致完成了,因为自己并没有这方面的经验,例如怎么token过期了前端怎么去刷新的问题,怎么扩展ocelot过期返回的response等等,自己都是要一点一点去学习了解的,这里只是贴出我的过程,和大家分享讨论下,希望可以给出好的意见。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2020/09/30 16:43

这里作为后续的更新,

之前我已经讲过如何认证token,然后我下午又想了一个刷新token的方法,讲一下我的思路。

首先是认证服务器返回token,过期时间,token创建时间3个字段,然后前端存储这3个字段。

例如当一次请求的时候token为空,则请求token并存储,第二次请求时如果token有值,则根据过期时间,token创建时间来判断是否过期没如果过期了就在此请求token,并且更新当前的localStorge,代码如下

/**

* 字符串转日期

*/

function stringToDate(str){

var tempStrs = str.split(" ");

var dateStrs = tempStrs[0].split("-");

var year = parseInt(dateStrs[0], 10);

var month = parseInt(dateStrs[1], 10) - 1;

var day = parseInt(dateStrs[2], 10);

var timeStrs = tempStrs[1].split(":");

var hour = parseInt(timeStrs [0], 10);

var minute = parseInt(timeStrs[1], 10);

var second = parseInt(timeStrs[2], 10);

var date = new Date(year, month, day, hour, minute, second);

return date;

}

/**

* 全局ajax处理

*/

layui.use('layer', function () {

var layer = layui.layer;

$.ajaxSetup({

cache: false,

beforeSend: function (xhr) {

var token = localStorage.getItem('token');//token

var tokenExpireTime = localStorage.getItem('tokenExpireTime');//过期时间

var tokenSaveTime = localStorage.getItem('tokenSaveTime');//token保存时间

var requestToken = false;//是否需要获取token

if (token == undefined || tokenExpireTime == undefined || tokenSaveTime == null) {

requestToken = true;

}

if (!requestToken) {//不需要时判断token是否过期

if (tokenExpireTime == undefined) {

requestToken = true;

}

else {

var now = new Date();

tokenSaveTime = stringToDate(tokenSaveTime);

var s=now.getTime()-tokenSaveTime.getTime();

//计算出相差天数

var days=Math.floor(s/(24*3600*1000))

//计算出小时数

var leave1=s%(24*3600*1000) //计算天数后剩余的毫秒数

var hours=Math.floor(leave1/(3600*1000))

//计算相差分钟数

var leave2=leave1%(3600*1000) //计算小时数后剩余的毫秒数

var minutes=Math.floor(leave2/(60*1000))

//计算相差秒数

//var leave3=leave2%(60*1000) //计算分钟数后剩余的毫秒数

//var seconds=Math.round(leave3/1000)

if(days>0)

{

requestToken = true

}

else if(hours>0){

requestToken = true

}

else if(minutes>tokenExpireTime){

requestToken = true

}

}

}

if (requestToken) {

$.ajax({

url: api + '/auth/token',

type: 'get',

datatype: 'json',

async:false,

beforeSend: function () {

var i=1;防止调用token时会通过ajaxStup再次执行beforeSend

},

success: function (res) {

if (res.code == 200) {

token=res.data.token;

localStorage.setItem('token', res.data.token);

localStorage.setItem('tokenExpireTime', res.data.expireMinutes);

localStorage.setItem('tokenSaveTime', res.data.createTime);

}

},

complete: function () {

var i=1;

}

})

}

xhr.setRequestHeader('Authorization', 'Bearer ' + token);

},

error: function (request) {

layer.msg('响应服务器失败', {

icon: 7

});

},

});

})

你可能感兴趣的:(#,微服务学习)