鼎鼎知识库
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.

20IdentityServer在API中的实现.md 9.3KB

преди 5 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. 在研究IdentityServer4的过程中遇到过两个问题:
  2. - 在IdentityServer4通常的资料是针对Web的,而不是通过接口
  3. - .Net Core 2.1 2.0有些地方和.NET和.Net Core 2.0都不一样
  4. 正好发现一个项目是在 .Net Core写,并且IdentityServer4提供接口。项目地址:https://github.com/marklaygo/Angular-IdentityServer-WebAPI
  5. 首先来看`Config.cs`
  6. ```
  7. public static class Config
  8. {
  9. //User
  10. public static List<TestUser> GetUsers()
  11. {
  12. return new List<TestUser>{
  13. new TestUser{
  14. SubjectId="1",
  15. Username="",
  16. Password=""
  17. }
  18. };
  19. }
  20. //IdentityResource
  21. public static IEnumerable<IdentityResource> GetIdentityResources()
  22. {
  23. return new IdentityResource[]{
  24. new IdentityResources.OpenId(),
  25. new IdentityResources.Profile();
  26. };
  27. }
  28. //API
  29. public static IEnumerable<ApiResource> GetApis()
  30. {
  31. return new ApiResource[]{
  32. new ApiResource("api1", "webApi"),
  33. new ApiResource("accountApi", "AccountApi") //这里的api给验证服务器自己用,因为验证服务器通过接口开发给外界
  34. };
  35. }
  36. //Client
  37. public static IEnumerable<Client> GetClients()
  38. {
  39. renturn new Client[]
  40. {
  41. new Client
  42. {
  43. ClientId="Angular",
  44. AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
  45. RequireClientSecret = fasle,
  46. AllowAccessTokenViaBrowser = true,
  47. AccessTokenLifetime = 300,
  48. AllowOfflineAccess = true,
  49. AllowedScopes = {
  50. IdentityServerConstants.StandardScopes.OpenId,//客户端要的和IdentityServer4给的保持一致
  51. IdentityServerConstatns.StandardScopes.Profile,//客户端要的和IdentityServer4给的保持一致
  52. "api1",//api告诉Client
  53. "accountApi"//api告诉Client
  54. }
  55. }
  56. };
  57. }
  58. }
  59. ```
  60. 再来看上下文,一定是继承`IdentityDbContext`。
  61. ```
  62. public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
  63. {
  64. public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options){
  65. }
  66. protected override void OnModelCreating(ModelBuilder builder)
  67. {
  68. base.OnModelCreating(builder);
  69. }
  70. }
  71. public class ApplicationUser : IdentityUser
  72. {
  73. }
  74. ```
  75. 有关对外开放的claim,通过实现`IProfileService`接口。
  76. ```
  77. public class IdentityProfileSerivce : IProfileService
  78. {
  79. private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
  80. private readonly UserManager<ApplicationUser> _userManager;
  81. public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicaitonUser> claimsFactory, UserManager<ApplicationUser> userManager)
  82. {
  83. _claimsFactory = claimsFactory;
  84. _userManager = userManager;
  85. }
  86. public async Task GetProfileDataAsync(ProfileDataRequestContext context)
  87. {
  88. var subjectId = context.Subject.GetSubjectId();
  89. var user = awati _userManager.FindByIdAsync(subjectId);
  90. var principal = await _claimsFactory.CreateAsync(user);
  91. var claims = principal.Claims.ToList();
  92. claims.Add(new Claim(JwtClaimTypes.Role, "test_role"));
  93. context.IssuedClaims = claim;
  94. }
  95. public async Task IsActiveAsync(IsActiveContext context)
  96. {
  97. var subjectId = context.Subject.GetSubjectId();
  98. var user = awati _userManager.FindByIdAsync(subjectId);
  99. context.IsActive = user != null;
  100. }
  101. }
  102. ```
  103. `Startup.cs`配置文件
  104. ```
  105. public class Startup
  106. {
  107. public IConfiguration Configuration{get;}
  108. public IHostingEnvironment Environment{get;}
  109. public Startup(IConfiguraiotn configuraiotn, IHostingEnvironment environment)
  110. {
  111. Configuraiton = configuration;
  112. Envrionment = environment;
  113. }
  114. public void ConfigureService(IServiceCollection services)
  115. {
  116. //上下文
  117. services.AddDbContext<ApplicationDbContext>(options => options.UserSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  118. //Identity
  119. servcies.AddIdentity<ApplicationUser, IdentityRole>()
  120. .AddEntityFrameworkeStore<ApplicationDbContext>()
  121. .AddDefaultTokenProviders();
  122. //有关Password的配置
  123. services.COnfigure<IdentityOptions>(options => {
  124. options.Password.RequrieDigit = false;
  125. options.Password.RequrieLength =4;
  126. options.Password.ReqruieNonAlphanumeric = false;
  127. options.Password.RequireUppercase = false;
  128. options.Password.RequrieLowercase = false;
  129. })
  130. //mvc
  131. services.AddMvcCore()
  132. .AddAuthorization()
  133. .AddJsonFormatters();
  134. //对外开发的claim
  135. services.AddTransient<IProfileService, IdentityProfileService>();
  136. //http://localhost:5000验证服务器的地址
  137. //验证服务器的配置
  138. var builder = service.AddIdentityServer()
  139. .AddInMemoryIndentityRecouse(Config.GetIdenityResource())
  140. .AddInMemoryApiResource(Config.GetApis())
  141. .AddInMemoryClients(Config.GetClients())
  142. .AddAspNetIdentity<ApplicationUser>()
  143. .AddProfileService<IdentityProfileService>();
  144. //验证服务器接口本身也需要验证
  145. services.AddAuthetnication("Bearer")
  146. .AddJwtBearer("Bearer", options => {
  147. options.Authority = "http://localhost:5000";
  148. options.RequireHttpsMetadata = false;
  149. options.Audience = "accountApi";
  150. options.TokenValidatioinParamsters = new TokenValidationParameters()
  151. {
  152. ClockSkew = TimeSpan.FromMinutes(0)
  153. }
  154. });
  155. //cors
  156. services.AddCors(options => {
  157. options.AddPolicy("default", policy => {
  158. policy.WithOrigin("http://localhost:4200")
  159. .AllowAnyHeader()
  160. .AllowAnyMethod();
  161. });
  162. });
  163. if(Environment.IsDevelopment())
  164. {
  165. builder.AddDeveloperSigningCredential();
  166. }
  167. else
  168. {
  169. throw new Exception("need to configure key material");
  170. }
  171. }
  172. public void Configure(IApplicationBuilder app, ApplicationDbContext context)
  173. {
  174. if(Environment.IsDevelopment())
  175. {
  176. app.UseDeveloperExceptionPage();
  177. }
  178. app.UseCors("default");
  179. app.UseIdentityServer();
  180. app.UseAuthentication();
  181. app.UseMvc();
  182. //种子化数据
  183. try
  184. {
  185. context.Database.Migrate();
  186. context.Users.RemoveRange(contexts.Users.ToList());
  187. context.SaveChanges();
  188. var user = new ApplicationUser
  189. {
  190. UserName = "",
  191. NormalizedUserName = "",
  192. Email = "",
  193. NormalizedEmail = "",
  194. EmailConfirmed = true,
  195. LockoutEnabled = fasle,
  196. SecurityStamp = Guid.NewGuid().ToString()
  197. };
  198. if(!context.Users.Any(u =>u.UserName == user.UserName))
  199. {
  200. var passwordHasher = new PasswordHasher<ApplicatonUser>();
  201. var hased = passwordHasher.HashPassword(user,"");
  202. user.PasswordHash = hased;
  203. var userStore = new UserStore<ApplicationUser>(context);
  204. userStore.CreateAsync(user);
  205. }
  206. }
  207. catch(Exception ex)
  208. {
  209. }
  210. }
  211. }
  212. ```
  213. 模型
  214. ```
  215. public class RegisterViewModel
  216. {
  217. [Required]
  218. [EmailAddress]
  219. publci string Email{get;set;}
  220. [Requried]
  221. [DataType(DataType.Password)]
  222. public string Password{get;set;}
  223. }
  224. public class ChangePasswordViewModel
  225. {
  226. [Required]
  227. [DataType(DataType.EmailAddress)]
  228. public string Email{get;set;}
  229. [Required]
  230. [DataType(DataType.Password)]
  231. public string OldPassword{get;set;}
  232. [Requried]
  233. [DataType(DataType.Password)]
  234. public string NewPassword{get;set;}
  235. }
  236. ```
  237. 最后来到有关用户管理的控制器。
  238. ```
  239. [Route("api/[controller]")]
  240. publci class AccountController : ControllerBase
  241. {
  242. private readonly UserManager<ApplicationUser> _userManager;
  243. private readonly IUserClaimPrincipalFactory<ApplicatioinUser> _claimsFactory;
  244. //构造函数略
  245. [HttpPost("Register")]
  246. public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
  247. {
  248. if(ModelState.IsValid)
  249. {
  250. var user = new ApplicationUser {UserName = model.Email, Email = model.Email};
  251. var result = awati _userManager.CreateAsync(user, model.Password);
  252. return new JsonResult(result);
  253. }
  254. return BadRequest();
  255. }
  256. [HttpPost("ChangePassword")]
  257. [Authorize(AuthenticationSchemes = "Bearer")]
  258. public async Task<IActionResult> ChangePassword([FromBody]ChangePasswordViewModel model)
  259. {
  260. if(ModelState.IsValid)
  261. {
  262. var user = await _userManager.FindByEmailAsync(model.Email);
  263. if(user!=null)
  264. {
  265. var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
  266. return new JsonResult(result);
  267. }
  268. }
  269. return BadRequest();
  270. }
  271. }
  272. ```