You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
9.9 KiB
280 lines
9.9 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Security.Claims; |
|
using System.Threading.Tasks; |
|
using AX.FireTrainingSys.DTOs; |
|
using AX.FireTrainingSys.Models; |
|
using AX.FireTrainingSys.Services; |
|
using Microsoft.AspNetCore.Authorization; |
|
using Microsoft.AspNetCore.Http; |
|
using Microsoft.AspNetCore.Mvc; |
|
using Microsoft.AspNetCore.Mvc.ModelBinding; |
|
using Microsoft.EntityFrameworkCore; |
|
using Microsoft.Extensions.Caching.Memory; |
|
using Microsoft.Extensions.Logging; |
|
using Microsoft.Extensions.Options; |
|
using static BCrypt.Net.BCrypt; |
|
|
|
namespace AX.FireTrainingSys.Controllers |
|
{ |
|
/// <summary> |
|
/// 帐号控制器。 |
|
/// </summary> |
|
[Produces("application/json")] |
|
[Route("api/[controller]")] |
|
[ApiVersion("1.0")] |
|
//[Authorize(Roles = "Profile")] |
|
[ApiController] |
|
public class AccountController : ControllerBase |
|
{ |
|
private readonly IOptionsMonitor<JwtOptions> jwtOptions; |
|
private readonly IJwtService jwtService; |
|
private readonly IMemoryCache memoryCache; |
|
private readonly DriveDbContext dbContext; |
|
|
|
|
|
public AccountController(IJwtService jwtService, |
|
IOptionsMonitor<JwtOptions> jwtOptions, |
|
IMemoryCache memoryCache, |
|
DriveDbContext dbContext) |
|
{ |
|
this.jwtService = jwtService; |
|
this.jwtOptions = jwtOptions; |
|
this.memoryCache = memoryCache; |
|
this.dbContext = dbContext; |
|
} |
|
|
|
/// <summary> |
|
/// 登录系统。 |
|
/// </summary> |
|
/// <param name="roleType">角色类型</param> |
|
/// <param name="info">登录信息</param> |
|
/// <returns></returns> |
|
//[ProducesResponseType(ErrorCodes.E600)] |
|
//[ProducesResponseType(ErrorCodes.E611)] |
|
//[ProducesResponseType(ErrorCodes.E612)] |
|
[ProducesResponseType(StatusCodes.Status400BadRequest)] |
|
[ProducesResponseType(StatusCodes.Status200OK)] |
|
[AllowAnonymous] |
|
[HttpPost("SignIn")] |
|
public async Task<ActionResult<IdentityInfo>> AccountSignIn([FromQuery, BindRequired] RoleType roleType, [FromBody] SignInInfo info) |
|
{ |
|
var name = info.Name; |
|
|
|
var user = await dbContext.Users |
|
.AsNoTracking() |
|
.Where(e => e.Name == name && e.RoleType == roleType) |
|
.FirstOrDefaultAsync(); |
|
|
|
if (user == default) |
|
return this.ErrorCode(ErrorCodes.E611); |
|
|
|
if (!Verify(info.Password, user.Password)) |
|
return this.ErrorCode(ErrorCodes.E611); |
|
|
|
if (!user.Enabled) |
|
return this.ErrorCode(ErrorCodes.E612); |
|
|
|
var userid = user.Id; |
|
var realname = user.RealName; |
|
var rolename = default(string); |
|
|
|
if (user.RoleType == RoleType.Admin) |
|
rolename = nameof(RoleType.Admin); |
|
else if (user.RoleType == RoleType.Teacher) |
|
rolename = nameof(RoleType.Teacher); |
|
else |
|
rolename = nameof(RoleType.Student); |
|
|
|
var claims = new[] |
|
{ |
|
new Claim(JwtClaimTypes.Subject, userid), |
|
new Claim(JwtClaimTypes.Name, realname), |
|
new Claim(JwtClaimTypes.Role, rolename), |
|
new Claim(JwtClaimTypes.Role, "Profile") |
|
}; |
|
|
|
var identity = new ClaimsIdentity(claims); |
|
|
|
var token = jwtService.Create(identity); |
|
var refreshToken = Guid.NewGuid().ToString("N"); |
|
var options = jwtOptions.CurrentValue; |
|
|
|
memoryCache.Set(refreshToken, userid, DateTimeOffset.Now.AddMinutes(options.RefreshExpires)); |
|
|
|
var result = new IdentityInfo() |
|
{ |
|
Token = token, |
|
RefreshToken = refreshToken, |
|
Expires = options.Expires, |
|
UserId = userid, |
|
RealName = realname, |
|
RoleType = roleType |
|
}; |
|
|
|
return Ok(result); |
|
} |
|
|
|
/// <summary> |
|
/// 登出系统。 |
|
/// </summary> |
|
/// <returns></returns> |
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)] |
|
[ProducesResponseType(StatusCodes.Status200OK)] |
|
[HttpPost("SignOut")] |
|
public Microsoft.AspNetCore.Mvc.ActionResult AccountSignOut() |
|
{ |
|
//TODO: 把 JWT 放入黑名单,其它地方则验证黑名单,将来再处理 |
|
return Ok(); |
|
} |
|
|
|
/// <summary> |
|
/// 修改密码。 |
|
/// </summary> |
|
/// <param name="info">修改密码信息</param> |
|
//[ProducesResponseType(ErrorCodes.E611)] |
|
//[ProducesResponseType(ErrorCodes.E612)] |
|
[ProducesResponseType(StatusCodes.Status400BadRequest)] |
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)] |
|
[ProducesResponseType(StatusCodes.Status403Forbidden)] |
|
[ProducesResponseType(StatusCodes.Status404NotFound)] |
|
[ProducesResponseType(StatusCodes.Status200OK)] |
|
[HttpPut("[action]")] |
|
public async Task<Microsoft.AspNetCore.Mvc.ActionResult> Password([FromBody] ModifyPasswordInfo info) |
|
{ |
|
var userid = HttpContext.User.FindFirstValue(JwtClaimTypes.Subject); |
|
|
|
var user = await dbContext.Users.FirstOrDefaultAsync(e => e.Id == userid); |
|
|
|
if (user == null) |
|
return NotFound(); |
|
|
|
if (!Verify(info.Password, user.Password)) |
|
return this.ErrorCode(ErrorCodes.E611); |
|
|
|
if (!user.Enabled) |
|
return this.ErrorCode(ErrorCodes.E612); |
|
|
|
user.Password = HashPassword(info.NewPassword); |
|
|
|
await dbContext.SaveChangesAsync(); |
|
|
|
return Ok(); |
|
} |
|
|
|
/// <summary> |
|
/// 刷新令牌。 |
|
/// </summary> |
|
//[ProducesResponseType(ErrorCodes.E613)] |
|
//[ProducesResponseType(ErrorCodes.E614)] |
|
[ProducesResponseType(StatusCodes.Status200OK)] |
|
[AllowAnonymous] |
|
[HttpPost("[action]")] |
|
public async Task<ActionResult<IdentityInfo>> RefreshToken([FromBody] RefreshTokenInfo info) |
|
{ |
|
if (string.IsNullOrEmpty(info.RefreshToken) || |
|
string.IsNullOrEmpty(info.Token)) |
|
return this.ErrorCode(ErrorCodes.E613); |
|
|
|
//校验缓存中是否有该刷新令牌 |
|
if (memoryCache.TryGetValue<string>(info.RefreshToken, out var userid)) |
|
{ |
|
//校验令牌是否有效 |
|
if (!jwtService.Validate(info.Token, out var principal)) |
|
return this.ErrorCode(ErrorCodes.E613); |
|
|
|
var options = jwtOptions.CurrentValue; |
|
var identity = principal.Identity as ClaimsIdentity; |
|
var realname = principal.FindFirstValue(JwtClaimTypes.Name); |
|
var roletype = RoleType.Student; |
|
|
|
if (principal.IsInRole(nameof(RoleType.Admin))) |
|
roletype = RoleType.Admin; |
|
else if (principal.IsInRole(nameof(RoleType.Teacher))) |
|
roletype = RoleType.Teacher; |
|
else if (principal.IsInRole(nameof(RoleType.Student))) |
|
roletype = RoleType.Student; |
|
|
|
var newToken = jwtService.Create(identity); |
|
|
|
var result = new IdentityInfo() |
|
{ |
|
Token = newToken, |
|
RefreshToken = info.RefreshToken, |
|
Expires = options.Expires, |
|
RealName = realname, |
|
RoleType = roletype |
|
}; |
|
|
|
return Ok(result); |
|
} |
|
|
|
//专用于数据同步 |
|
//假设刷新令牌和令牌都有效,模拟登录流程 |
|
try |
|
{ |
|
var jwt = jwtService.Decode(info.Token); |
|
var name = jwt.Claims.FirstOrDefault(e => e.Type == JwtClaimTypes.Name)?.Value; |
|
|
|
if (name is null) |
|
return this.ErrorCode(ErrorCodes.E613); |
|
|
|
var user = await dbContext.Users |
|
.AsNoTracking() |
|
.Where(e => e.Name == name) |
|
.FirstOrDefaultAsync(); |
|
|
|
if (user == default) |
|
return this.ErrorCode(ErrorCodes.E611); |
|
|
|
if (!user.Enabled) |
|
return this.ErrorCode(ErrorCodes.E612); |
|
|
|
userid = user.Id; |
|
var roleType = user.RoleType; |
|
var realname = user.RealName; |
|
var rolename = default(string); |
|
|
|
if (roleType == RoleType.Admin) |
|
rolename = nameof(RoleType.Admin); |
|
else if (roleType == RoleType.Teacher) |
|
rolename = nameof(RoleType.Teacher); |
|
else |
|
rolename = nameof(RoleType.Student); |
|
|
|
var claims = new[] |
|
{ |
|
new Claim(JwtClaimTypes.Subject, userid), |
|
new Claim(JwtClaimTypes.Name, realname), |
|
new Claim(JwtClaimTypes.Role, rolename), |
|
new Claim(JwtClaimTypes.Role, "Profile") |
|
}; |
|
|
|
var identity = new ClaimsIdentity(claims); |
|
|
|
var token = jwtService.Create(identity); |
|
var refreshToken = Guid.NewGuid().ToString("N"); |
|
var options = jwtOptions.CurrentValue; |
|
|
|
memoryCache.Set(refreshToken, userid, DateTimeOffset.Now.AddMinutes(options.RefreshExpires)); |
|
|
|
var result = new IdentityInfo() |
|
{ |
|
Token = token, |
|
RefreshToken = refreshToken, |
|
Expires = options.Expires, |
|
UserId = userid, |
|
RealName = realname, |
|
RoleType = roleType |
|
}; |
|
|
|
return Ok(result); |
|
} |
|
catch |
|
{ |
|
return this.ErrorCode(ErrorCodes.E613); |
|
} |
|
} |
|
} |
|
}
|
|
|