我这里是用JWT机制、IdentityServer4、Cookie来做单点登陆的,由于cookie本身的局限性,目前只能在同域名下单点登陆,如果有想做不同域名的可以访问我的上一篇,即登陆后在Headers中携带Authorization:Bearer Token密钥进行登陆。
以登录cookie为例说明
1,共享之间的二级域名必须是解析于同一个顶级域名之下;
2,比如现在有两个二级域名,a.xxx.com(域名A)和b.xxx.com(域名B)。两个都解析于域名xxx.com
顶级域名之下。
3,现在域名A的登录cookie域名B下面需要使用。
4,域名A的登录cookie把cookie域设置成xxx.com(注意不要写成www.xxx.com,因为www.xxx.com
也属于二级域名,不是顶级域名),path设置成’/’
5,此时域名B下面可以读取域名A写入的cookie
6,这样就可以实现域名A的cookie被域名B共享
7,如果要实现相互共享,就把两个域名的cookie域都设置成xxx.com
有了这种思路我们就可以在项目中来设置相应属性来实现了。
主要登陆代码如下:
/// <summary>
/// SSO单点登陆 jwt写入cookie数据
/// </summary>
/// <param name="account"></param>
/// <param name="password"></param>
/// <param name="redirectUrl"></param>
/// <returns></returns>
[HttpPost]
public IActionResult WriteInToken(User user1)
{
if (user1.userName == "admin" || user1.userName == "张三" &&
user1.passWord == "123456") //实际数据库校验
{
//string name = this.UserContext.User.UserName;
User user = new User()
{
userId = 1000,
account = user1.userName,
userName = user1.userName,
age = 18,
email = "zhangsan@qq.com"
};
string token = new JWTService().GetJwtToken(user);
CookieOptions cookieOptions = new CookieOptions();
cookieOptions.Domain = "openwebsite.cn"; //设置domain让cookie共享
cookieOptions.Path = "/";
cookieOptions.HttpOnly = true;
HttpContext.Response.Cookies.Append("openwebsite", token, cookieOptions);
////把token写入cookie
//Response.Cookies.Append("openwebsite", token);
//登录成功跳转到对应系统
user1.redirectUrl += $"https://jwttwo.openwebsite.cn/?Authorization={token}";
// return Redirect(user1.redirectUrl);
//return RedirectToAction("LoginYZ", "Home");
return Json(token);
}
else
{
//登录失败
return Json("登录失败");
}
}
这里就设置了cookie作用域以及cookie的名称、数据。同时还设置了JWT的有效期等。
Startup启动文件设置:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(option =>
{
//登陆过滤器
option.Filters.Add(new ApiAuthorizationFilter());
//option.Filters.Add(new ApiResultFilter());
//option.Filters.Add(new BusinessLogicExceptionFilter());
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var serverSecret = new SymmetricSecurityKey(Encoding.UTF8.
GetBytes(Configuration["Jwt:Secret"]));
options.TokenValidationParameters = new
TokenValidationParameters
{
ValidateIssuer = false,//是否验证Issuer
ValidIssuer = Configuration["Jwt:Issuer"],
ValidateAudience = true,//是否验证Audience
ValidAudience = Configuration["Jwt:Audience"],
ValidateLifetime = true,//是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(5),
ValidateIssuerSigningKey = true,//是否验证SecurityKey
IssuerSigningKey = serverSecret
};
options.Events = new JwtBearerEvents()
{
//首次收到协议消息时调用。
OnMessageReceived = context =>
{
//携带Authorization
//string token = context.Request.Query["Authorization"];
//if (!string.IsNullOrEmpty(token))
//{
// string jmtoken = DecodeJwt.decode(token);
// JWT wT = JsonConvert.DeserializeObject<JWT>(jmtoken);
// DateTime dtStart = JWTService.StampToDateTime(wT.exp);
// if (dtStart >= DateTime.Now)
// {
// context.Token = token;
// }
//}
//获取cookie 单点登陆则获取cookie
context.Token = context.Request.Cookies["openwebsite"];
//获取headers中携带的Authorization 非单点登陆咋取headers中携带的数据
//context.Token = context.Request.Query["Authorization"];
return Task.CompletedTask;
},
//如果在请求处理过程中身份验证失败,则调用
OnAuthenticationFailed = context =>
{
//如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
//context.Response.Headers.Add("Token-Expired", "true");
//自定义自己想要返回的数据结果,我这里要返回的是Json对象,通过引用Newtonsoft.Json库进行转换
var payload = JsonConvert.SerializeObject(new { Code = "402",
Message = "很抱歉,Token无效或已过期" });
//自定义返回的数据类型
context.Response.ContentType = "application/json";
//自定义返回状态码,默认为401 我这里改成 200
context.Response.StatusCode = StatusCodes.Status200OK;
//输出Json数据结果
context.Response.WriteAsync(payload);
// return Task.FromResult(0);
}
return Task.CompletedTask;
},
//在安全令牌已通过验证并生成 ClaimsIdentity 后调用。
OnTokenValidated = context =>
{
return Task.CompletedTask;
}
};
});
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Shared/Error");
}
app.UseStaticFiles();
///添加中间件(Middleware) 启用验证
app.UseAuthentication();
//路由
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Login}/{id?}");
});
//}
}
}
这两块就是主要代码,有需要的可以下载源码自己去研究研究。