优化Swagger

1.swagger登录可以用用户账号登录,如果登录成功 token存在session中 之前默认admin感觉没什么用 当然也可以扩展User 加个字段是否开发者帐户等类似的
2.优化权限校验 优先读取Header->没有读取Session 中token解析用户
This commit is contained in:
LemonNoCry 2023-05-24 11:19:36 +08:00
parent a979d36461
commit 0cea9672b5
No known key found for this signature in database
11 changed files with 774 additions and 598 deletions

View File

@ -177,7 +177,7 @@
登录管理【无权限】
</summary>
</member>
<member name="M:Blog.Core.Controllers.LoginController.#ctor(Blog.Core.IServices.ISysUserInfoServices,Blog.Core.IServices.IUserRoleServices,Blog.Core.IServices.IRoleServices,Blog.Core.AuthHelper.PermissionRequirement,Blog.Core.IServices.IRoleModulePermissionServices)">
<member name="M:Blog.Core.Controllers.LoginController.#ctor(Blog.Core.IServices.ISysUserInfoServices,Blog.Core.IServices.IUserRoleServices,Blog.Core.IServices.IRoleServices,Blog.Core.AuthHelper.PermissionRequirement,Blog.Core.IServices.IRoleModulePermissionServices,Microsoft.Extensions.Logging.ILogger{Blog.Core.Controllers.LoginController})">
<summary>
构造函数注入
</summary>

View File

@ -9,307 +9,330 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Blog.Core.Common.Swagger;
using Serilog;
namespace Blog.Core.Controllers
{
/// <summary>
/// 登录管理【无权限】
/// </summary>
[Produces("application/json")]
[Route("api/Login")]
[AllowAnonymous]
public class LoginController : BaseApiController
{
readonly ISysUserInfoServices _sysUserInfoServices;
readonly IUserRoleServices _userRoleServices;
readonly IRoleServices _roleServices;
readonly PermissionRequirement _requirement;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
/// <summary>
/// 登录管理【无权限】
/// </summary>
[Produces("application/json")]
[Route("api/Login")]
[AllowAnonymous]
public class LoginController : BaseApiController
{
readonly ISysUserInfoServices _sysUserInfoServices;
readonly IUserRoleServices _userRoleServices;
readonly IRoleServices _roleServices;
readonly PermissionRequirement _requirement;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
private readonly ILogger<LoginController> _logger;
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="sysUserInfoServices"></param>
/// <param name="userRoleServices"></param>
/// <param name="roleServices"></param>
/// <param name="requirement"></param>
/// <param name="roleModulePermissionServices"></param>
public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices,
IRoleServices roleServices, PermissionRequirement requirement,
IRoleModulePermissionServices roleModulePermissionServices, ILogger<LoginController> logger)
{
this._sysUserInfoServices = sysUserInfoServices;
this._userRoleServices = userRoleServices;
this._roleServices = roleServices;
_requirement = requirement;
_roleModulePermissionServices = roleModulePermissionServices;
_logger = logger;
}
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="sysUserInfoServices"></param>
/// <param name="userRoleServices"></param>
/// <param name="roleServices"></param>
/// <param name="requirement"></param>
/// <param name="roleModulePermissionServices"></param>
public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices, IRoleServices roleServices, PermissionRequirement requirement, IRoleModulePermissionServices roleModulePermissionServices)
{
this._sysUserInfoServices = sysUserInfoServices;
this._userRoleServices = userRoleServices;
this._roleServices = roleServices;
_requirement = requirement;
_roleModulePermissionServices = roleModulePermissionServices;
}
#region token的第1种方法
/// <summary>
/// 获取JWT的方法1
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("Token")]
public async Task<MessageModel<string>> GetJwtStr(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
//这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass));
if (user != null)
{
TokenModelJwt tokenModel = new TokenModelJwt {Uid = 1, Role = user};
jwtStr = JwtHelper.IssueJwt(tokenModel);
suc = true;
}
else
{
jwtStr = "login fail!!!";
}
return new MessageModel<string>()
{
success = suc,
msg = suc ? "获取成功" : "获取失败",
response = jwtStr
};
}
#region token的第1种方法
/// <summary>
/// 获取JWT的方法2给Nuxt提供
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("GetTokenNuxt")]
public MessageModel<string> GetJwtStrForNuxt(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
//这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
//这里直接写死了
if (name == "admins" && pass == "admins")
{
TokenModelJwt tokenModel = new TokenModelJwt
{
Uid = 1,
Role = "Admin"
};
/// <summary>
/// 获取JWT的方法1
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("Token")]
public async Task<MessageModel<string>> GetJwtStr(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
//这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
jwtStr = JwtHelper.IssueJwt(tokenModel);
suc = true;
}
else
{
jwtStr = "login fail!!!";
}
var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass));
if (user != null)
{
TokenModelJwt tokenModel = new TokenModelJwt { Uid = 1, Role = user };
var result = new
{
data = new {success = suc, token = jwtStr}
};
jwtStr = JwtHelper.IssueJwt(tokenModel);
suc = true;
}
else
{
jwtStr = "login fail!!!";
}
return new MessageModel<string>()
{
success = suc,
msg = suc ? "获取成功" : "获取失败",
response = jwtStr
};
}
return new MessageModel<string>()
{
success = suc,
msg = suc ? "获取成功" : "获取失败",
response = jwtStr
};
}
#endregion
/// <summary>
/// 获取JWT的方法2给Nuxt提供
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("GetTokenNuxt")]
public MessageModel<string> GetJwtStrForNuxt(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
//这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
//这里直接写死了
if (name == "admins" && pass == "admins")
{
TokenModelJwt tokenModel = new TokenModelJwt
{
Uid = 1,
Role = "Admin"
};
/// <summary>
/// 获取JWT的方法3整个系统主要方法
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("JWTToken3.0")]
public async Task<MessageModel<TokenInfoViewModel>> GetJwtToken3(string name = "", string pass = "")
{
string jwtStr = string.Empty;
jwtStr = JwtHelper.IssueJwt(tokenModel);
suc = true;
}
else
{
jwtStr = "login fail!!!";
}
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass))
return Failed<TokenInfoViewModel>("用户名或密码不能为空");
var result = new
{
data = new { success = suc, token = jwtStr }
};
pass = MD5Helper.MD5Encrypt32(pass);
return new MessageModel<string>()
{
success = suc,
msg = suc ? "获取成功" : "获取失败",
response = jwtStr
};
}
#endregion
var user = await _sysUserInfoServices.Query(d =>
d.LoginName == name && d.LoginPWD == pass && d.IsDeleted == false);
if (user.Count > 0)
{
var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, name),
new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()),
new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()),
new Claim(ClaimTypes.Expiration,
DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
};
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
/// <summary>
/// 获取JWT的方法3整个系统主要方法
/// </summary>
/// <param name="name"></param>
/// <param name="pass"></param>
/// <returns></returns>
[HttpGet]
[Route("JWTToken3.0")]
public async Task<MessageModel<TokenInfoViewModel>> GetJwtToken3(string name = "", string pass = "")
{
string jwtStr = string.Empty;
// ids4和jwt切换
// jwt
if (!Permissions.IsUseIds4)
{
var data = await _roleModulePermissionServices.RoleModuleMaps();
var list = (from item in data
where item.IsDeleted == false
orderby item.Id
select new PermissionItem
{
Url = item.Module?.LinkUrl,
Role = item.Role?.Name.ObjToString(),
}).ToList();
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass))
return Failed<TokenInfoViewModel>("用户名或密码不能为空");
_requirement.Permissions = list;
}
pass = MD5Helper.MD5Encrypt32(pass);
var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
return Success(token, "获取成功");
}
else
{
return Failed<TokenInfoViewModel>("认证失败");
}
}
var user = await _sysUserInfoServices.Query(d => d.LoginName == name && d.LoginPWD == pass && d.IsDeleted == false);
if (user.Count > 0)
{
var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, name),
new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()),
new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
};
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
/// <summary>
/// 请求刷新Token以旧换新
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[Route("RefreshToken")]
public async Task<MessageModel<TokenInfoViewModel>> RefreshToken(string token = "")
{
string jwtStr = string.Empty;
if (string.IsNullOrEmpty(token))
return Failed<TokenInfoViewModel>("token无效请重新登录");
var tokenModel = JwtHelper.SerializeJwt(token);
if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0)
{
var user = await _sysUserInfoServices.QueryById(tokenModel.Uid);
var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
if (value != null && user.CriticalModifyTime > value.ObjToDate())
{
return Failed<TokenInfoViewModel>("很抱歉,授权已失效,请重新授权!");
}
if (user != null && !(value != null && user.CriticalModifyTime > value.ObjToDate()))
{
var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.LoginName),
new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()),
new Claim(ClaimTypes.Expiration,
DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
};
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//用户标识
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
identity.AddClaims(claims);
var refreshToken = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
return Success(refreshToken, "获取成功");
}
}
return Failed<TokenInfoViewModel>("认证失败!");
}
/// <summary>
/// 获取JWT的方法4给 JSONP 测试
/// </summary>
/// <param name="callBack"></param>
/// <param name="id"></param>
/// <param name="sub"></param>
/// <param name="expiresSliding"></param>
/// <param name="expiresAbsoulute"></param>
/// <returns></returns>
[HttpGet]
[Route("jsonp")]
public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30,
int expiresAbsoulute = 30)
{
TokenModelJwt tokenModel = new TokenModelJwt
{
Uid = id,
Role = sub
};
string jwtStr = JwtHelper.IssueJwt(tokenModel);
string response = string.Format("\"value\":\"{0}\"", jwtStr);
string call = callBack + "({" + response + "})";
Response.WriteAsync(call);
}
// ids4和jwt切换
// jwt
if (!Permissions.IsUseIds4)
{
var data = await _roleModulePermissionServices.RoleModuleMaps();
var list = (from item in data
where item.IsDeleted == false
orderby item.Id
select new PermissionItem
{
Url = item.Module?.LinkUrl,
Role = item.Role?.Name.ObjToString(),
}).ToList();
/// <summary>
/// 测试 MD5 加密字符串
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
[HttpGet]
[Route("Md5Password")]
public string Md5Password(string password = "")
{
return MD5Helper.MD5Encrypt32(password);
}
_requirement.Permissions = list;
}
/// <summary>
/// swagger登录
/// </summary>
/// <param name="loginRequest"></param>
/// <returns></returns>
[HttpPost]
[Route("/api/Login/swgLogin")]
public async Task<dynamic> SwgLogin([FromBody] SwaggerLoginRequest loginRequest)
{
if (loginRequest is null)
{
return new {result = false};
}
var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
return Success(token, "获取成功");
}
else
{
return Failed<TokenInfoViewModel>("认证失败");
}
}
try
{
var result = await GetJwtToken3(loginRequest.name, loginRequest.pwd);
if (result.success)
{
HttpContext.SuccessSwagger();
HttpContext.SuccessSwaggerJwt(result.response.token);
return new {result = true};
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Swagger登录异常");
}
/// <summary>
/// 请求刷新Token以旧换新
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[Route("RefreshToken")]
public async Task<MessageModel<TokenInfoViewModel>> RefreshToken(string token = "")
{
string jwtStr = string.Empty;
return new {result = false};
}
if (string.IsNullOrEmpty(token))
return Failed<TokenInfoViewModel>("token无效请重新登录");
var tokenModel = JwtHelper.SerializeJwt(token);
if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0)
{
var user = await _sysUserInfoServices.QueryById(tokenModel.Uid);
var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
if (value != null && user.CriticalModifyTime > value.ObjToDate())
{
return Failed<TokenInfoViewModel>("很抱歉,授权已失效,请重新授权!");
}
/// <summary>
/// weixin登录
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("wxLogin")]
public dynamic WxLogin(string g = "", string token = "")
{
return new {g, token};
}
}
if (user != null && !(value != null && user.CriticalModifyTime > value.ObjToDate()))
{
var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD);
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.LoginName),
new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
};
claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//用户标识
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
identity.AddClaims(claims);
var refreshToken = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
return Success(refreshToken, "获取成功");
}
}
return Failed<TokenInfoViewModel>("认证失败!");
}
/// <summary>
/// 获取JWT的方法4给 JSONP 测试
/// </summary>
/// <param name="callBack"></param>
/// <param name="id"></param>
/// <param name="sub"></param>
/// <param name="expiresSliding"></param>
/// <param name="expiresAbsoulute"></param>
/// <returns></returns>
[HttpGet]
[Route("jsonp")]
public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, int expiresAbsoulute = 30)
{
TokenModelJwt tokenModel = new TokenModelJwt
{
Uid = id,
Role = sub
};
string jwtStr = JwtHelper.IssueJwt(tokenModel);
string response = string.Format("\"value\":\"{0}\"", jwtStr);
string call = callBack + "({" + response + "})";
Response.WriteAsync(call);
}
/// <summary>
/// 测试 MD5 加密字符串
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
[HttpGet]
[Route("Md5Password")]
public string Md5Password(string password = "")
{
return MD5Helper.MD5Encrypt32(password);
}
/// <summary>
/// swagger登录
/// </summary>
/// <param name="loginRequest"></param>
/// <returns></returns>
[HttpPost]
[Route("/api/Login/swgLogin")]
public dynamic SwgLogin([FromBody] SwaggerLoginRequest loginRequest)
{
// 这里可以查询数据库等各种校验
if (loginRequest?.name == "admin" && loginRequest?.pwd == "admin")
{
HttpContext.Session.SetString("swagger-code", "success");
return new { result = true };
}
return new { result = false };
}
/// <summary>
/// weixin登录
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("wxLogin")]
public dynamic WxLogin(string g = "", string token = "")
{
return new { g, token };
}
}
public class SwaggerLoginRequest
{
public string name { get; set; }
public string pwd { get; set; }
}
public class SwaggerLoginRequest
{
public string name { get; set; }
public string pwd { get; set; }
}
}

View File

@ -47,7 +47,7 @@
},
"LogToDb": true,
"LogAOP": {
"Enabled": true,
"Enabled": false,
"LogToFile": {
"Enabled": true
},

View File

@ -1,128 +1,141 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta charset="utf-8"/>
<title>登录 - 接口文档</title>
<script src="js/jquery-3.3.1.min.js"></script>
<link type="text/css" href="css/style.css" rel="stylesheet" />
<link type="text/css" href="css/style.css" rel="stylesheet"/>
<script src="js/anime.min.js"></script>
</head>
<body>
<div class="page">
<div class="container">
<div class="left">
<div></div>
<div class="page">
<div class="container">
<div class="left">
<div></div>
<div class="login"><img src="/logo.jpg.jpg" height="30" alt="Alternate Text"/> Blog.Core 接口文档</div>
<div class="eula">欢迎使用!</div>
<div class="eula">用户名admin密码admin</div>
</div>
<div class="right">
<svg viewBox="0 0 320 300">
<defs>
<linearGradient inkscape:collect="always"
id="linearGradient"
x1="13"
y1="193.49992"
x2="307"
y2="193.49992"
gradientUnits="userSpaceOnUse">
<stop style="stop-color:#ff00ff;"
offset="0"
id="stop876" />
<stop style="stop-color:#ff0000;"
offset="1"
id="stop878" />
</linearGradient>
</defs>
<path d="m 40,120.00016 239.99984,-3.2e-4 c 0,0 24.99263,0.79932 25.00016,35.00016 0.008,34.20084 -25.00016,35 -25.00016,35 h -239.99984 c 0,-0.0205 -25,4.01348 -25,38.5 0,34.48652 25,38.5 25,38.5 h 215 c 0,0 20,-0.99604 20,-25 0,-24.00396 -20,-25 -20,-25 h -190 c 0,0 -20,1.71033 -20,25 0,24.00396 20,25 20,25 h 168.57143" />
</svg>
<div class="form">
<label for="email">用户名</label>
<input type="email" id="email">
<label for="password">密码</label>
<input type="password" id="password">
<input type="submit" id="submit" onclick="submit()" value="登 录">
</div>
<div class="login"><img src="/logo.jpg.jpg" height="30" alt="Alternate Text"/> Blog.Core 接口文档</div>
<div class="eula">欢迎使用!</div>
<div class="eula">使用用户账号登录</div>
</div>
<div class="right">
<svg viewBox="0 0 320 300">
<defs>
<linearGradient inkscape:collect="always"
id="linearGradient"
x1="13"
y1="193.49992"
x2="307"
y2="193.49992"
gradientUnits="userSpaceOnUse">
<stop style="stop-color:#ff00ff;"
offset="0"
id="stop876"/>
<stop style="stop-color:#ff0000;"
offset="1"
id="stop878"/>
</linearGradient>
</defs>
<path d="m 40,120.00016 239.99984,-3.2e-4 c 0,0 24.99263,0.79932 25.00016,35.00016 0.008,34.20084 -25.00016,35 -25.00016,35 h -239.99984 c 0,-0.0205 -25,4.01348 -25,38.5 0,34.48652 25,38.5 25,38.5 h 215 c 0,0 20,-0.99604 20,-25 0,-24.00396 -20,-25 -20,-25 h -190 c 0,0 -20,1.71033 -20,25 0,24.00396 20,25 20,25 h 168.57143"/>
</svg>
<div class="form">
<label for="email">用户名</label>
<input type="email" id="email">
<label for="password">密码</label>
<input type="password" id="password">
<input type="submit" id="submit" onclick="submit()" value="登 录">
</div>
</div>
</div>
</div>
<script>
var current = null;
document.querySelector('#email').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: 0,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '240 1386',
duration: 700,
easing: 'easeOutQuart'
}
});
});
document.querySelector('#password').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: -336,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '240 1386',
duration: 700,
easing: 'easeOutQuart'
}
});
});
document.querySelector('#submit').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: -730,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '530 1386',
duration: 700,
easing: 'easeOutQuart'
}
});
});
function submit() {
let postdata = {
"name": $("#email").val(),
"pwd": $("#password").val(),
};
if (!(postdata.name && postdata.pwd)) {
alert('参数不正确');
return
<script>
var current = null;
document.querySelector('#email').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: 0,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '240 1386',
duration: 700,
easing: 'easeOutQuart'
}
$.ajax({
url: "/api/Login/swgLogin",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(postdata),
dataType: 'json',
success: function (data) {
if (data?.result) {
window.location.href = "/index.html";
} else {
alert('参数不正确');
}
}
});
});
});
document.querySelector('#password').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: -336,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '240 1386',
duration: 700,
easing: 'easeOutQuart'
}
});
});
document.querySelector('#submit').addEventListener('focus', function (e) {
if (current) current.pause();
current = anime({
targets: 'path',
strokeDashoffset: {
value: -730,
duration: 700,
easing: 'easeOutQuart'
},
strokeDasharray: {
value: '530 1386',
duration: 700,
easing: 'easeOutQuart'
}
});
});
function GetQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return decodeURI(r[2]);
return null;
}
function submit() {
let postdata = {
"name": $("#email").val(),
"pwd": $("#password").val(),
};
if (!(postdata.name && postdata.pwd)) {
alert('参数不正确');
return
}
</script>
$.ajax({
url: "/api/Login/swgLogin",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(postdata),
dataType: 'json',
success: function (data) {
if (data?.result) {
var returnUrl = GetQueryString("returnUrl");
if (returnUrl != null && returnUrl.length > 0) {
window.location.href = returnUrl;
} else {
window.location.href = "/index.html";
}
} else {
alert('参数不正确');
}
}
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
using System;
using Microsoft.AspNetCore.Http;
namespace Blog.Core.Common.Extensions;
public static class HttpContextExtension
{
public static ISession GetSession(this HttpContext context)
{
try
{
return context.Session;
}
catch (Exception)
{
return default;
}
}
}

View File

@ -1,7 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using Blog.Core.Common.Swagger;
using Blog.Core.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@ -51,7 +53,25 @@ namespace Blog.Core.Common.HttpContextUser
public string GetToken()
{
return _accessor.HttpContext?.Request?.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
var token = _accessor.HttpContext?.Request?.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
if (!token.IsNullOrEmpty())
{
return token;
}
if (_accessor.HttpContext?.IsSuccessSwagger() == true)
{
token = _accessor.HttpContext.GetSuccessSwaggerJwt();
if (token.IsNotEmptyOrNull())
{
var claims = new ClaimsIdentity(GetClaimsIdentity(token));
_accessor.HttpContext.User.AddIdentity(claims);
return token;
}
}
return token;
}
public List<string> GetUserInfoFromToken(string ClaimType)
@ -77,6 +97,10 @@ namespace Blog.Core.Common.HttpContextUser
public IEnumerable<Claim> GetClaimsIdentity()
{
if (_accessor.HttpContext == null) return ArraySegment<Claim>.Empty;
if (!IsAuthenticated()) return GetClaimsIdentity(GetToken());
var claims = _accessor.HttpContext.User.Claims.ToList();
var headers = _accessor.HttpContext.Request.Headers;
foreach (var header in headers)
@ -86,6 +110,19 @@ namespace Blog.Core.Common.HttpContextUser
return claims;
}
public IEnumerable<Claim> GetClaimsIdentity(string token)
{
var jwtHandler = new JwtSecurityTokenHandler();
// token校验
if (token.IsNotEmptyOrNull() && jwtHandler.CanReadToken(token))
{
var jwtToken = jwtHandler.ReadJwtToken(token);
return jwtToken.Claims;
}
return new List<Claim>();
}
public List<string> GetClaimValueByType(string ClaimType)
{

View File

@ -0,0 +1,48 @@
using Blog.Core.Common.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
namespace Blog.Core.Common.Swagger;
public static class SwaggerContextExtension
{
public const string SwaggerCodeKey = "swagger-code";
public const string SwaggerJwt = "swagger-jwt";
public static bool IsSuccessSwagger()
{
return App.HttpContext?.GetSession()?.GetString(SwaggerCodeKey) == "success";
}
public static bool IsSuccessSwagger(this HttpContext context)
{
return context.GetSession()?.GetString(SwaggerCodeKey) == "success";
}
public static void SuccessSwagger()
{
App.HttpContext?.GetSession()?.SetString(SwaggerCodeKey, "success");
}
public static void SuccessSwagger(this HttpContext context)
{
context.GetSession()?.SetString(SwaggerCodeKey, "success");
}
public static void SuccessSwaggerJwt(this HttpContext context, string token)
{
context.GetSession()?.SetString(SwaggerJwt, token);
}
public static string GetSuccessSwaggerJwt(this HttpContext context)
{
return context.GetSession()?.GetString(SwaggerJwt);
}
public static void RedirectSwaggerLogin(this HttpContext context)
{
var returnUrl = context.Request.GetDisplayUrl(); //获取当前url地址
context.Response.Redirect("/swg-login.html?returnUrl=" + returnUrl);
}
}

View File

@ -14,6 +14,7 @@ using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Blog.Core.Common.Swagger;
using Blog.Core.Model.Models;
namespace Blog.Core.AuthHelper
@ -116,7 +117,7 @@ namespace Blog.Core.AuthHelper
return;
}
}
//判断请求是否拥有凭据,即有没有登录
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
@ -127,10 +128,79 @@ namespace Blog.Core.AuthHelper
var isTestCurrent = AppSettings.app(new string[] {"AppSettings", "UseLoadTest"}).ObjToBool();
//result?.Principal不为空即登录成功
if (result?.Principal != null || isTestCurrent)
if (result?.Principal != null || isTestCurrent || httpContext.IsSuccessSwagger())
{
if (!isTestCurrent) httpContext.User = result.Principal;
//应该要先校验用户的信息 再校验菜单权限相关的
//校验用户
var user = await _userServices.QueryById(_user.ID, true);
if (user == null)
{
_user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户不存在或已被删除").MessageModel;
context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
return;
}
if (user.IsDeleted)
{
_user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被删除,禁止登陆!").MessageModel;
context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
return;
}
if (!user.Enable)
{
_user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被禁用!禁止登陆!").MessageModel;
context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
return;
}
// 判断token是否过期过期则重新登录
var isExp = false;
// ids4和jwt切换
// ids4
if (Permissions.IsUseIds4)
{
isExp = (httpContext.User.Claims.FirstOrDefault(s => s.Type == "exp")?.Value) != null &&
DateHelper.StampToDateTime(httpContext.User.Claims
.FirstOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now;
}
else
{
// jwt
isExp =
(httpContext.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.Expiration)
?.Value) != null &&
DateTime.Parse(httpContext.User.Claims
.FirstOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now;
}
if (!isExp)
{
context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权"));
return;
}
//校验签发时间
if (!Permissions.IsUseIds4)
{
var value = httpContext.User.Claims
.FirstOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
if (value != null)
{
if (user.CriticalModifyTime > value.ObjToDate())
{
_user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权")
.MessageModel;
context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
return;
}
}
}
// 获取当前用户的角色信息
var currentUserRoles = new List<string>();
// ids4和jwt切换
@ -153,7 +223,8 @@ namespace Blog.Core.AuthHelper
if (currentUserRoles.All(s => s != "SuperAdmin"))
{
var isMatchRole = false;
var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
var permisssionRoles =
requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
foreach (var item in permisssionRoles)
{
try
@ -177,50 +248,7 @@ namespace Blog.Core.AuthHelper
return;
}
}
// 判断token是否过期过期则重新登录
var isExp = false;
// ids4和jwt切换
// ids4
if (Permissions.IsUseIds4)
{
isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) != null &&
DateHelper.StampToDateTime(httpContext.User.Claims
.SingleOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now;
}
else
{
// jwt
isExp =
(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)
?.Value) != null &&
DateTime.Parse(httpContext.User.Claims
.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now;
}
if (!isExp)
{
context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权"));
return;
}
//校验签发时间
if (!Permissions.IsUseIds4)
{
var value = httpContext.User.Claims
.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
if (value != null)
{
var user = await _userServices.QueryById(_user.ID, true);
if (user.CriticalModifyTime > value.ObjToDate())
{
_user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权")
.MessageModel;
context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
return;
}
}
}
context.Succeed(requirement);
return;

View File

@ -1,5 +1,6 @@
using System.Net;
using System.Threading.Tasks;
using Blog.Core.Common.Swagger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@ -28,7 +29,7 @@ namespace Blog.Core.Extensions.Middlewares
}
// 无权限跳转swagger登录页
context.Response.Redirect("/swg-login.html");
context.RedirectSwaggerLogin();
}
else
{
@ -40,7 +41,7 @@ namespace Blog.Core.Extensions.Middlewares
{
// 使用session模式
// 可以使用其他的
return context.Session.GetString("swagger-code") == "success";
return context.IsSuccessSwagger();
}
/// <summary>

View File

@ -4,138 +4,141 @@ using System.Collections.Generic;
namespace Blog.Core.Model.Models
{
/// <summary>
/// 用户信息表
/// </summary>
//[SugarTable("SysUserInfo")]
[SugarTable("SysUserInfo", "用户表")] //('数据库表名''数据库表备注')
public class SysUserInfo : SysUserInfoRoot<long>
{
public SysUserInfo()
{
}
/// <summary>
/// 用户信息表
/// </summary>
//[SugarTable("SysUserInfo")]
[SugarTable("SysUserInfo", "用户表")] //('数据库表名''数据库表备注')
public class SysUserInfo : SysUserInfoRoot<long>
{
public SysUserInfo()
{
}
public SysUserInfo(string loginName, string loginPWD)
{
LoginName = loginName;
LoginPWD = loginPWD;
RealName = LoginName;
Status = 0;
CreateTime = DateTime.Now;
UpdateTime = DateTime.Now;
LastErrorTime = DateTime.Now;
ErrorCount = 0;
Name = "";
}
public SysUserInfo(string loginName, string loginPWD)
{
LoginName = loginName;
LoginPWD = loginPWD;
RealName = LoginName;
Status = 0;
CreateTime = DateTime.Now;
UpdateTime = DateTime.Now;
LastErrorTime = DateTime.Now;
ErrorCount = 0;
Name = "";
}
/// <summary>
/// 登录账号
/// </summary>
[SugarColumn(Length = 200, IsNullable = true, ColumnDescription = "登录账号")]
//:eg model 根据sqlsugar的完整定义可以如下定义ColumnDescription可定义表字段备注
//[SugarColumn(IsNullable = false, ColumnDescription = "登录账号", IsPrimaryKey = false, IsIdentity = false, Length = 50)]
//ColumnDescription 表字段备注, 已在MSSQL测试配合 [SugarTable("SysUserInfo", "用户表")]//('数据库表名''数据库表备注')
//可以完整生成 表备注和各个字段的中文备注
//2022/10/11
//测试mssql 发现 不写ColumnDescription写好注释在mssql下也能生成表字段备注
public string LoginName { get; set; }
/// <summary>
/// 登录账号
/// </summary>
[SugarColumn(Length = 200, IsNullable = true, ColumnDescription = "登录账号")]
//:eg model 根据sqlsugar的完整定义可以如下定义ColumnDescription可定义表字段备注
//[SugarColumn(IsNullable = false, ColumnDescription = "登录账号", IsPrimaryKey = false, IsIdentity = false, Length = 50)]
//ColumnDescription 表字段备注, 已在MSSQL测试配合 [SugarTable("SysUserInfo", "用户表")]//('数据库表名''数据库表备注')
//可以完整生成 表备注和各个字段的中文备注
//2022/10/11
//测试mssql 发现 不写ColumnDescription写好注释在mssql下也能生成表字段备注
public string LoginName { get; set; }
/// <summary>
/// 登录密码
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string LoginPWD { get; set; }
/// <summary>
/// 登录密码
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string LoginPWD { get; set; }
/// <summary>
/// 真实姓名
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string RealName { get; set; }
/// <summary>
/// 真实姓名
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string RealName { get; set; }
/// <summary>
/// 状态
/// </summary>
public int Status { get; set; }
/// <summary>
/// 状态
/// </summary>
public int Status { get; set; }
/// <summary>
/// 部门
/// </summary>
[SugarColumn(IsNullable = true)]
public int DepartmentId { get; set; } = -1;
/// <summary>
/// 部门
/// </summary>
[SugarColumn(IsNullable = true)]
public int DepartmentId { get; set; } = -1;
/// <summary>
/// 备注
/// </summary>
[SugarColumn(Length = 2000, IsNullable = true)]
public string Remark { get; set; }
/// <summary>
/// 备注
/// </summary>
[SugarColumn(Length = 2000, IsNullable = true)]
public string Remark { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; } = DateTime.Now;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; } = DateTime.Now;
/// <summary>
/// 关键业务修改时间
/// </summary>
public DateTime CriticalModifyTime { get; set; } = DateTime.Now;
/// <summary>
/// 关键业务修改时间
/// </summary>
public DateTime CriticalModifyTime { get; set; } = DateTime.Now;
/// <summary>
///最后异常时间
/// </summary>
public DateTime LastErrorTime { get; set; } = DateTime.Now;
/// <summary>
///最后异常时间
/// </summary>
public DateTime LastErrorTime { get; set; } = DateTime.Now;
/// <summary>
///错误次数
/// </summary>
public int ErrorCount { get; set; }
/// <summary>
///错误次数
/// </summary>
public int ErrorCount { get; set; }
/// <summary>
/// 登录账号
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string Name { get; set; }
/// <summary>
/// 登录账号
/// </summary>
[SugarColumn(Length = 200, IsNullable = true)]
public string Name { get; set; }
// 性别
[SugarColumn(IsNullable = true)]
public int Sex { get; set; } = 0;
// 性别
[SugarColumn(IsNullable = true)]
public int Sex { get; set; } = 0;
// 年龄
[SugarColumn(IsNullable = true)]
public int Age { get; set; }
// 年龄
[SugarColumn(IsNullable = true)]
public int Age { get; set; }
// 生日
[SugarColumn(IsNullable = true)]
public DateTime Birth { get; set; } = DateTime.Now;
// 生日
[SugarColumn(IsNullable = true)]
public DateTime Birth { get; set; } = DateTime.Now;
// 地址
[SugarColumn(Length = 200, IsNullable = true)]
public string Address { get; set; }
// 地址
[SugarColumn(Length = 200, IsNullable = true)]
public string Address { get; set; }
[SugarColumn(IsNullable = true)]
public bool IsDeleted { get; set; }
[SugarColumn(DefaultValue = "1")]
public bool Enable { get; set; } = true;
/// <summary>
/// 租户Id
/// </summary>
[SugarColumn(IsNullable = false,DefaultValue = "0")]
public long TenantId { get; set; }
[SugarColumn(IsNullable = true)]
public bool IsDeleted { get; set; }
[Navigate(NavigateType.OneToOne, nameof(TenantId))]
public SysTenant Tenant { get; set; }
/// <summary>
/// 租户Id
/// </summary>
[SugarColumn(IsNullable = false, DefaultValue = "0")]
public long TenantId { get; set; }
[SugarColumn(IsIgnore = true)]
public List<string> RoleNames { get; set; }
[Navigate(NavigateType.OneToOne, nameof(TenantId))]
public SysTenant Tenant { get; set; }
[SugarColumn(IsIgnore = true)]
public List<long> Dids { get; set; }
[SugarColumn(IsIgnore = true)]
public List<string> RoleNames { get; set; }
[SugarColumn(IsIgnore = true)]
public string DepartmentName { get; set; }
}
[SugarColumn(IsIgnore = true)]
public List<long> Dids { get; set; }
[SugarColumn(IsIgnore = true)]
public string DepartmentName { get; set; }
}
}

View File

@ -3,72 +3,76 @@ using Blog.Core.IServices;
using Xunit;
using Autofac;
using Blog.Core.AuthHelper;
using Microsoft.Extensions.Logging;
namespace Blog.Core.Tests
{
public class LoginController_Should
{
LoginController loginController;
public class LoginController_Should
{
LoginController loginController;
private readonly ISysUserInfoServices _sysUserInfoServices;
private readonly IUserRoleServices _userRoleServices;
private readonly IRoleServices _roleServices;
private readonly PermissionRequirement _requirement;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
private readonly ISysUserInfoServices _sysUserInfoServices;
private readonly IUserRoleServices _userRoleServices;
private readonly IRoleServices _roleServices;
private readonly PermissionRequirement _requirement;
private readonly IRoleModulePermissionServices _roleModulePermissionServices;
private readonly ILogger<LoginController> _logger;
DI_Test dI_Test = new DI_Test();
DI_Test dI_Test = new DI_Test();
public LoginController_Should()
{
var container = dI_Test.DICollections();
_sysUserInfoServices = container.Resolve<ISysUserInfoServices>();
_userRoleServices = container.Resolve<IUserRoleServices>();
_roleServices = container.Resolve<IRoleServices>();
_requirement = container.Resolve<PermissionRequirement>();
_roleModulePermissionServices = container.Resolve<IRoleModulePermissionServices>();
_logger = container.Resolve<ILogger<LoginController>>();
loginController = new LoginController(_sysUserInfoServices, _userRoleServices, _roleServices, _requirement,
_roleModulePermissionServices, _logger);
}
public LoginController_Should()
{
var container = dI_Test.DICollections();
_sysUserInfoServices = container.Resolve<ISysUserInfoServices>();
_userRoleServices = container.Resolve<IUserRoleServices>();
_roleServices = container.Resolve<IRoleServices>();
_requirement = container.Resolve<PermissionRequirement>();
_roleModulePermissionServices = container.Resolve<IRoleModulePermissionServices>();
loginController = new LoginController(_sysUserInfoServices,_userRoleServices,_roleServices,_requirement, _roleModulePermissionServices);
}
[Fact]
public void GetJwtStrTest()
{
var data = loginController.GetJwtStr("test", "test");
[Fact]
public void GetJwtStrTest()
{
var data = loginController.GetJwtStr("test", "test");
Assert.NotNull(data);
}
Assert.NotNull(data);
}
[Fact]
public void GetJwtStrForNuxtTest()
{
object blogs = loginController.GetJwtStrForNuxt("test", "test");
[Fact]
public void GetJwtStrForNuxtTest()
{
object blogs = loginController.GetJwtStrForNuxt("test", "test");
Assert.NotNull(blogs);
}
Assert.NotNull(blogs);
}
[Fact]
public async void GetJwtToken3Test()
{
[Fact]
public async void GetJwtToken3Test()
{
var res = await loginController.GetJwtToken3("test", "test");
var res = await loginController.GetJwtToken3("test", "test");
Assert.NotNull(res);
}
Assert.NotNull(res);
}
[Fact]
public async void RefreshTokenTest()
{
var res = await loginController.RefreshToken(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImp0aSI6IjgiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2V4cGlyYXRpb24iOiIyMDE5LzEwLzE4IDIzOjI2OjQ5IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5UZXN0IiwibmJmIjoxNTcxNDA4ODA5LCJleHAiOjE1NzE0MTI0MDksImlzcyI6IkJsb2cuQ29yZSIsImF1ZCI6IndyIn0.oz-SPz6UCL78fM09bUecw5rmjcNYEY9dWGtuPs2gdBg");
[Fact]
public async void RefreshTokenTest()
{
var res = await loginController.RefreshToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImp0aSI6IjgiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2V4cGlyYXRpb24iOiIyMDE5LzEwLzE4IDIzOjI2OjQ5IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5UZXN0IiwibmJmIjoxNTcxNDA4ODA5LCJleHAiOjE1NzE0MTI0MDksImlzcyI6IkJsb2cuQ29yZSIsImF1ZCI6IndyIn0.oz-SPz6UCL78fM09bUecw5rmjcNYEY9dWGtuPs2gdBg");
Assert.NotNull(res);
}
Assert.NotNull(res);
}
[Fact]
public void Md5PasswordTest()
{
var res = loginController.Md5Password("test");
[Fact]
public void Md5PasswordTest()
{
var res = loginController.Md5Password("test");
Assert.NotNull(res);
}
}
}
Assert.NotNull(res);
}
}
}