Commit d38321e2 by ryun

OAuth授权对接

parent f4bf0f8b
### appsetting配置文件
```
Application:OpenAntiSqlInject Bollean // 是否开启反SQL注入 默认关闭 true 开启 false 关闭
Application:AntiSqlInjectRouteWhite String[] // 开启反SQL注入白名单地址
AppSQLEncrypt:IsEncryption Bollean // 是否加密数据库密码 默认明文 true 加密 false 明文
AppSQLEncrypt:TempMySqlConnectionString String // MySql连接模板字符串 不配置则使用默认
AppSQLEncrypt:TempSqlServerConnectionString String // SqlServer连接模板字符串 不配置则使用默认
AppSQLEncrypt:TempOracleConnectionString String // Oracle连接模板字符串 不配置则使用默认
oauth2:authorize_url String // 获取授权码URL(必填无默认值)
oauth2:token_url String // 获取令牌URL(必填无默认值)
oauth2:get_user_info_url String // 获取用户属性信息URL(必填无默认值)
oauth2:response_type String // 授权类型,此处的值固定为"code"。默认“code”
oauth2:redirect_uri String // 成功授权后的重定向地址,需要使用 urlencode 进行编码;默认空值
oauth2:scope String // 表示申请的权限范围;默认空值
oauth2:state String // 客户端请求中的 state 参数,返回的值和客户端请求时的值相同;默认随机
oauth2:grant_type String // 授权类型,此值固定为“authorization_code”。默认“authorization_code”
oauth2:client_id String // 客户端的 ID,在 IDTrust 上新建应用可以获取到。(必填无默认值)
oauth2:client_secret String // 客户端密钥,在 IDTrust 上新建应用可以获取到。(必填无默认值)
oauth2:test_mode Bollean // 测试模式;默认false
oauth2:default_mail String // 创建账号时默认邮箱;默认{0}@oauth.com
oauth2:default_password String // 创建账号时默认密码;默认123456
oauth2:default_hosid String // 创建账号时默认医院;默认自动获取,获取失败则0
```
\ No newline at end of file
using System;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.DtoModels.OAuth;
using Performance.Services;
namespace Performance.Api.Controllers
{
public class OAuthController : Controller
{
private readonly ILogger<OAuthController> _logger;
private readonly IOptions<Application> _options;
private readonly OAuthService _service;
public OAuthController(ILogger<OAuthController> logger, IOptions<Application> options, OAuthService service)
{
_logger = logger;
_options = options;
_service = service;
}
/// <summary>
/// 授权起始地址
/// </summary>
/// <returns></returns>
[AllowAnonymous, HttpGet, Route("api/oauth2/authorize")]
public async Task<ActionResult> Authorize()
{
_logger.LogInformation("OAuth授权启动");
try
{
var res = await _service.Authorize();
return (res.StatusCode == (int)HttpStatusCode.OK) ? Ok() : BadRequest();
}
catch (Exception ex)
{
_logger.LogError($"OAuth授权启动:请求异常={ex}");
}
return BadRequest();
}
/// <summary>
/// 获取授权码,初始化登录信息
/// </summary>
/// <param name="code"></param>
/// <param name="state"></param>
/// <returns></returns>
[AllowAnonymous, HttpPost, Route("api/oauth2/token")]
public async Task<ApiResponse<JwtToken>> Token([FromQuery] string code, [FromQuery] string state)
{
_logger.LogInformation($"OAuth授权接受回调请求:code={code}&state={state}");
try
{
var tokenResponse = await _service.Token(code, state);
if (tokenResponse.StatusCode != (int)HttpStatusCode.OK)
return new ApiResponse<JwtToken>(ResponseType.Fail, "授权中心Token获取错误");
if (tokenResponse.Data is not TokenResponse)
return new ApiResponse<JwtToken>(ResponseType.Fail, "授权中心Token数据无法解析");
var tokenData = tokenResponse.Data as TokenResponse;
var userResponse = await _service.GetUserInfo(tokenData.access_token);
if (userResponse.StatusCode != (int)HttpStatusCode.OK)
return new ApiResponse<JwtToken>(ResponseType.Fail, "授权中心AccessToken获取错误");
if (userResponse.Data is not OAuthUserInfoResponse)
return new ApiResponse<JwtToken>(ResponseType.Fail, "授权中心AccessToken数据无法解析");
var userData = userResponse.Data as OAuthUserInfoResponse;
var user = _service.GetOrInitUserInfo(userData);
var claims = new Claim[]
{
new Claim(JwtClaimTypes.Id, user.UserID.ToString()),
new Claim(JwtClaimTypes.Login, user.Login),
new Claim(JwtClaimTypes.RealName, user.RealName),
new Claim(JwtClaimTypes.Mail, user.Mail??""),
new Claim(JwtClaimTypes.AppName, ""),
new Claim(JwtClaimTypes.Device, ""),
new Claim(JwtClaimTypes.Department, user.Department ?? ""),
new Claim(JwtClaimTypes.QuickLogin, EQuickLogin.SSO.ToString()),
};
var jwtToken = JwtTokenHelper.GenerateToken(claims, _options.Value.ExpirationMinutes);
return new ApiResponse<JwtToken>(ResponseType.OK, jwtToken);
}
catch (Exception ex)
{
_logger.LogError($"OAuth授权接受回调请求:请求异常:{ex}");
}
return new ApiResponse<JwtToken>(ResponseType.Fail, "授权失败");
}
}
}
\ No newline at end of file
......@@ -33,7 +33,7 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
services.AddHttpClient();
// appsettings.json
services.AddAppSettingConfiguration(Configuration);
......
......@@ -7,9 +7,20 @@
},
"AppConnection": {
//"PerformanceConnectionString": "server=112.124.13.17;database=db_performance;uid=suvalue;pwd=suvalue2016;pooling=true;charset=utf8;convert zero datetime=true;port=3306;connection timeout=120;max pool size=512;allow user variables=true;",
"PerformanceConnectionString": "server=192.168.18.166;database=db_performance_screen;uid=root;pwd=1234qwer;pooling=true;charset=utf8;convert zero datetime=true;port=3306;connection timeout=120;max pool size=512;allow user variables=true;"
"PerformanceConnectionString": "server=192.168.18.166;database=db_test_beiliu;uid=root;pwd=1234qwer;pooling=true;charset=utf8;convert zero datetime=true;port=3306;connection timeout=120;max pool size=512;allow user variables=true;"
//"PerformanceConnectionString": "server=116.62.245.55;database=db_performance;uid=root;pwd=1234qwer;pooling=true;charset=utf8;convert zero datetime=true;port=3306;connection timeout=120;max pool size=512;allow user variables=true;"
},
"oauth2": {
"authorize_url": "http://192.168.18.166:8038/oauth2/authorize",
"token_url": "http://192.168.18.166:8038/oauth2/token",
"get_user_info_url": "http://192.168.18.166:8038/oauth2/get_user_info",
"client_id": 1,
"client_secret": 123456,
"redirect_uri": "www.baidu.com",
"default_mail": "",
"default_password": "222",
"default_hosid": "16"
},
"Application": {
// 是否开启反SQL注入 默认关闭 true 开启 false 关闭
"OpenAntiSqlInject": true,
......
......@@ -1813,6 +1813,20 @@
<param name="hospitalId"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.OAuthController.Authorize">
<summary>
授权起始地址
</summary>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.OAuthController.Token(System.String,System.String)">
<summary>
获取授权码,初始化登录信息
</summary>
<param name="code"></param>
<param name="state"></param>
<returns></returns>
</member>
<member name="T:Performance.Api.Controllers.OriginalController">
<summary>
原始数据修改
......
......@@ -64,6 +64,16 @@
开启反SQL注入白名单地址
</summary>
</member>
<member name="T:Performance.DtoModels.AppSettings.AppSQLEncrypt">
<summary>
数据库密码加密
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.AppSQLEncrypt.IsEncryption">
<summary>
是否加密 true 加密 false 明文
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.RateLimitingConfig.Endpoints">
<summary>
路径
......@@ -197,6 +207,51 @@
结果值
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.OAuthUserInfoResponse.name">
<summary>
用户名,新建应用时,可以通过字段映射配置指定该名字。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.OAuthUserInfoResponse.desc">
<summary>
用户描述,新建应用时,可以通过字段映射配置指定该名字。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.OAuthUserInfoResponse.group">
<summary>
用户所属组,新建应用时,可以通过字段映射配置指定该名字。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.OAuthUserInfoResponse.role">
<summary>
用户角色,新建应用时,可以通过字段映射配置指定该名字。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.TokenResponse.access_token">
<summary>
表示访问令牌,必选项。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.TokenResponse.token_type">
<summary>
表示令牌类型,该值大小写不敏感,必选项,可以是 bearer类型或 mac 类型,IDTrust 目前固定为 bearer 类型。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.TokenResponse.expires_in">
<summary>
表示过期时间,单位为秒。IDTrust 设备 token 有效期为 1小时。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.TokenResponse.refresh_token">
<summary>
表示更新令牌,用来获取下一次的访问令牌。
</summary>
</member>
<member name="P:Performance.DtoModels.OAuth.TokenResponse.scope">
<summary>
表示权限范围,如果与客户端申请的范围一致。
</summary>
</member>
<member name="P:Performance.DtoModels.PerAgainData.RowNumber">
<summary>
行号
......
......@@ -9193,6 +9193,11 @@
初始密码 1 初始 2 改过
</summary>
</member>
<member name="P:Performance.EntityModels.sys_user.Remark">
<summary>
备注
</summary>
</member>
<member name="T:Performance.EntityModels.sys_user_hospital">
<summary>
......
namespace Performance.DtoModels.OAuth
{
public interface IOAuthResponse { }
}
namespace Performance.DtoModels.OAuth
{
public class OAuthErrorResponse : IOAuthResponse
{
public int errcode { get; set; }
public string errmsg { get; set; }
}
}
namespace Performance.DtoModels.OAuth
{
public class OAuthResponse<T> where T : IOAuthResponse
{
public OAuthResponse(int statusCode, T? data)
{
StatusCode = statusCode;
Data = data;
}
public int StatusCode { get; set; }
public T? Data { get; set; }
}
}
namespace Performance.DtoModels.OAuth
{
public class OAuthUserInfoResponse : IOAuthResponse
{
/// <summary>
/// 用户名,新建应用时,可以通过字段映射配置指定该名字。
/// </summary>
public string name { get; set; }
/// <summary>
/// 用户描述,新建应用时,可以通过字段映射配置指定该名字。
/// </summary>
public string desc { get; set; }
/// <summary>
/// 用户所属组,新建应用时,可以通过字段映射配置指定该名字。
/// </summary>
public string group { get; set; }
/// <summary>
/// 用户角色,新建应用时,可以通过字段映射配置指定该名字。
/// </summary>
public string role { get; set; }
}
}
namespace Performance.DtoModels.OAuth
{
public class TokenResponse : IOAuthResponse
{
/// <summary>
/// 表示访问令牌,必选项。
/// </summary>
public string access_token { get; set; }
/// <summary>
/// 表示令牌类型,该值大小写不敏感,必选项,可以是 bearer类型或 mac 类型,IDTrust 目前固定为 bearer 类型。
/// </summary>
public string token_type { get; set; }
/// <summary>
/// 表示过期时间,单位为秒。IDTrust 设备 token 有效期为 1小时。
/// </summary>
public string? expires_in { get; set; }
/// <summary>
/// 表示更新令牌,用来获取下一次的访问令牌。
/// </summary>
public string refresh_token { get; set; }
/// <summary>
/// 表示权限范围,如果与客户端申请的范围一致。
/// </summary>
public string scope { get; set; }
}
}
......@@ -53,6 +53,7 @@ public class UserRequest
/// 用户科室
/// </summary>
public string Department { get; set; }
public string Remark { get; set; }
}
public class UserRequestValidator : AbstractValidator<UserRequest>
......
......@@ -87,5 +87,9 @@ public class sys_user
/// 初始密码 1 初始 2 改过
/// </summary>
public int IsInitialPassword { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
}
......@@ -102,4 +102,5 @@ public enum EQuickLogin
{
YES = 1,
NO = 2,
SSO = 3,
}
......@@ -110,6 +110,17 @@ public UserIdentity QuickLogin(int targetUserId, int loginUserId, string passwor
return data;
}
public UserIdentity GetUserIdentity(int userId)
{
var targetUser = _userRepository.GetEntity(t => t.ID == userId && t.IsDelete == 1);
if (targetUser == null)
throw new PerformanceException($"您要登录的用户信息有误,请检查后重试");
var data = _mapper.Map<UserIdentity>(targetUser);
data.Token = Guid.NewGuid().ToString("N");
return data;
}
/// <summary>
/// 获取用户第一个角色
/// </summary>
......@@ -276,7 +287,7 @@ public UserResponse UpdateSelf(UserRequest request)
if (null == user)
throw new PerformanceException($"用户不存在 UserId:{request.ID}");
var vlist = _userRepository.GetEntities(t => t.ID != user.ID && t.Login == request.Login && t.IsDelete == 1);
var vlist = _userRepository.GetEntities(t => t.ID != user.ID && t.Login == request.Login && (t.ParentID == null || t.ParentID == 0) && t.IsDelete == 1);
if (null != vlist && vlist.Count() > 0)
throw new PerformanceException("登录名重复");
......@@ -438,7 +449,7 @@ public UserResponse ResetPwd(int userId, int loginUserId, string password)
/// <param name="request"></param>
public UserResponse InsertUser(UserRequest request, int userid)
{
if (null != _userRepository.GetEntity(t => t.Login == request.Login && t.IsDelete == 1))
if (null != _userRepository.GetEntity(t => t.Login == request.Login && (t.ParentID == null || t.ParentID == 0) && t.IsDelete == 1))
throw new PerformanceException("登录名重复");
int[] roleArray = UnitTypeUtil.Maps.Keys.ToArray();
......@@ -452,6 +463,8 @@ public UserResponse InsertUser(UserRequest request, int userid)
user.States = (int)States.Enabled;
user.IsInitialPassword = (int)InitialPassword.初始;
user.Department = UnitTypeUtil.Maps.ContainsKey(request.RoleArr[0]) ? request.Department : "";
user.Remark = request.Remark;
if (UnitTypeUtil.Maps.ContainsKey(request.RoleArr[0]))
{
var uninTypes = UnitTypeUtil.GetMaps(request.RoleArr[0]);
......@@ -472,7 +485,7 @@ public UserResponse InsertUser(UserRequest request, int userid)
var userID = user.ID;
for (int i = 1; i < request.RoleArr.Length; i++)
{
user.Login = request.Login + i;
user.Login = request.Login;
user.ParentID = userID;
user.Department = UnitTypeUtil.Maps.ContainsKey(request.RoleArr[i]) ? request.Department : "";
if (roleArray.Contains(request.RoleArr[i]))
......@@ -513,7 +526,7 @@ public UserResponse UpdateUser(UserRequest request, int userId)
if (null == user) throw new PerformanceException($"当前用户不存在");
var vlist = _userRepository.GetEntities(t => t.ID != user.ID && t.Login == request.Login && t.IsDelete == 1);
var vlist = _userRepository.GetEntities(t => t.ID != user.ID && t.Login == request.Login && (t.ParentID == null || t.ParentID == 0) && t.IsDelete == 1);
if (null != vlist && vlist.Count() > 0) throw new PerformanceException("登录名重复");
user.Login = request.Login;
......@@ -521,6 +534,7 @@ public UserResponse UpdateUser(UserRequest request, int userId)
user.RealName = request.RealName;
user.Mail = request.Mail;
user.States = request.States;
user.Remark = request.Remark;
//Md5小写加密
user.Password = string.IsNullOrEmpty(request.Password) ? user.Password : PwdHelper.MD5AndSalt(request.Password);
user.Department = UnitTypeUtil.Maps.ContainsKey(request.RoleArr[0]) ? request.Department : "";
......@@ -567,12 +581,13 @@ public UserResponse UpdateUser(UserRequest request, int userId)
diffUser.CreateDate = DateTime.Now;
diffUser.CreateUser = user.CreateUser;
diffUser.IsDelete = 1;
diffUser.Login = userLogin + i;
diffUser.Login = userLogin;
diffUser.ParentID = userID;
diffUser.Mobile = request.Mobile;
diffUser.RealName = request.RealName;
diffUser.Mail = request.Mail;
diffUser.States = request.States;
diffUser.Remark = request.Remark;
//Md5小写加密
diffUser.Password = string.IsNullOrEmpty(request.Password) ? user.Password : PwdHelper.MD5AndSalt(request.Password);
diffUser.Department = UnitTypeUtil.Maps.ContainsKey(request.RoleArr[i]) ? request.Department : "";
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2026
# Visual Studio Version 17
VisualStudioVersion = 17.4.33403.182
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Performance.ConsoleApp", "Performance.ConsoleApp\Performance.ConsoleApp.csproj", "{59218B05-5CE3-4EC1-A304-28183F191A04}"
EndProject
......@@ -27,6 +27,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Performance.Api", "Performa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Performance.Extract.Api", "Performance.Extract.Api\Performance.Extract.Api.csproj", "{A7AE6D0F-7B11-4EEF-9FEA-A279001EA54D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.doc", "0.doc", "{A4520E21-1B77-47AA-9BDF-A61EEE005743}"
ProjectSection(SolutionItems) = preProject
..\README.md = ..\README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment