消防培训系统服务器
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.

281 lines
9.9 KiB

2 years ago
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);
}
}
}
}