|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- 在研究IdentityServer4的过程中遇到过两个问题:
-
- - 在IdentityServer4通常的资料是针对Web的,而不是通过接口
- - .Net Core 2.1 2.0有些地方和.NET和.Net Core 2.0都不一样
-
- 正好发现一个项目是在 .Net Core写,并且IdentityServer4提供接口。项目地址:https://github.com/marklaygo/Angular-IdentityServer-WebAPI
-
- 首先来看`Config.cs`
- ```
- public static class Config
- {
- //User
- public static List<TestUser> GetUsers()
- {
- return new List<TestUser>{
- new TestUser{
- SubjectId="1",
- Username="",
- Password=""
- }
- };
- }
-
- //IdentityResource
- public static IEnumerable<IdentityResource> GetIdentityResources()
- {
- return new IdentityResource[]{
- new IdentityResources.OpenId(),
- new IdentityResources.Profile();
- };
- }
-
- //API
- public static IEnumerable<ApiResource> GetApis()
- {
- return new ApiResource[]{
- new ApiResource("api1", "webApi"),
- new ApiResource("accountApi", "AccountApi") //这里的api给验证服务器自己用,因为验证服务器通过接口开发给外界
- };
- }
-
- //Client
- public static IEnumerable<Client> GetClients()
- {
- renturn new Client[]
- {
- new Client
- {
- ClientId="Angular",
- AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
- RequireClientSecret = fasle,
- AllowAccessTokenViaBrowser = true,
- AccessTokenLifetime = 300,
- AllowOfflineAccess = true,
- AllowedScopes = {
- IdentityServerConstants.StandardScopes.OpenId,//客户端要的和IdentityServer4给的保持一致
- IdentityServerConstatns.StandardScopes.Profile,//客户端要的和IdentityServer4给的保持一致
- "api1",//api告诉Client
- "accountApi"//api告诉Client
- }
- }
- };
- }
- }
- ```
-
- 再来看上下文,一定是继承`IdentityDbContext`。
-
- ```
- public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
- {
- public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options){
-
- }
-
- protected override void OnModelCreating(ModelBuilder builder)
- {
- base.OnModelCreating(builder);
- }
- }
-
- public class ApplicationUser : IdentityUser
- {
-
- }
-
- ```
-
- 有关对外开放的claim,通过实现`IProfileService`接口。
-
- ```
- public class IdentityProfileSerivce : IProfileService
- {
- private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
- private readonly UserManager<ApplicationUser> _userManager;
-
- public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicaitonUser> claimsFactory, UserManager<ApplicationUser> userManager)
- {
- _claimsFactory = claimsFactory;
- _userManager = userManager;
- }
-
- public async Task GetProfileDataAsync(ProfileDataRequestContext context)
- {
- var subjectId = context.Subject.GetSubjectId();
- var user = awati _userManager.FindByIdAsync(subjectId);
- var principal = await _claimsFactory.CreateAsync(user);
- var claims = principal.Claims.ToList();
- claims.Add(new Claim(JwtClaimTypes.Role, "test_role"));
- context.IssuedClaims = claim;
- }
-
- public async Task IsActiveAsync(IsActiveContext context)
- {
- var subjectId = context.Subject.GetSubjectId();
- var user = awati _userManager.FindByIdAsync(subjectId);
- context.IsActive = user != null;
- }
- }
- ```
-
- `Startup.cs`配置文件
-
- ```
- public class Startup
- {
- public IConfiguration Configuration{get;}
- public IHostingEnvironment Environment{get;}
-
- public Startup(IConfiguraiotn configuraiotn, IHostingEnvironment environment)
- {
- Configuraiton = configuration;
- Envrionment = environment;
- }
-
- public void ConfigureService(IServiceCollection services)
- {
- //上下文
- services.AddDbContext<ApplicationDbContext>(options => options.UserSqlServer(Configuration.GetConnectionString("DefaultConnection")));
-
- //Identity
- servcies.AddIdentity<ApplicationUser, IdentityRole>()
- .AddEntityFrameworkeStore<ApplicationDbContext>()
- .AddDefaultTokenProviders();
-
- //有关Password的配置
- services.COnfigure<IdentityOptions>(options => {
- options.Password.RequrieDigit = false;
- options.Password.RequrieLength =4;
- options.Password.ReqruieNonAlphanumeric = false;
- options.Password.RequireUppercase = false;
- options.Password.RequrieLowercase = false;
- })
-
- //mvc
- services.AddMvcCore()
- .AddAuthorization()
- .AddJsonFormatters();
-
- //对外开发的claim
- services.AddTransient<IProfileService, IdentityProfileService>();
-
- //http://localhost:5000验证服务器的地址
- //验证服务器的配置
- var builder = service.AddIdentityServer()
- .AddInMemoryIndentityRecouse(Config.GetIdenityResource())
- .AddInMemoryApiResource(Config.GetApis())
- .AddInMemoryClients(Config.GetClients())
- .AddAspNetIdentity<ApplicationUser>()
- .AddProfileService<IdentityProfileService>();
-
- //验证服务器接口本身也需要验证
- services.AddAuthetnication("Bearer")
- .AddJwtBearer("Bearer", options => {
- options.Authority = "http://localhost:5000";
- options.RequireHttpsMetadata = false;
- options.Audience = "accountApi";
- options.TokenValidatioinParamsters = new TokenValidationParameters()
- {
- ClockSkew = TimeSpan.FromMinutes(0)
- }
- });
-
- //cors
- services.AddCors(options => {
-
- options.AddPolicy("default", policy => {
- policy.WithOrigin("http://localhost:4200")
- .AllowAnyHeader()
- .AllowAnyMethod();
- });
- });
-
- if(Environment.IsDevelopment())
- {
- builder.AddDeveloperSigningCredential();
- }
- else
- {
- throw new Exception("need to configure key material");
- }
-
- }
-
- public void Configure(IApplicationBuilder app, ApplicationDbContext context)
- {
- if(Environment.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseCors("default");
- app.UseIdentityServer();
- app.UseAuthentication();
- app.UseMvc();
-
- //种子化数据
- try
- {
- context.Database.Migrate();
- context.Users.RemoveRange(contexts.Users.ToList());
- context.SaveChanges();
-
- var user = new ApplicationUser
- {
- UserName = "",
- NormalizedUserName = "",
- Email = "",
- NormalizedEmail = "",
- EmailConfirmed = true,
- LockoutEnabled = fasle,
- SecurityStamp = Guid.NewGuid().ToString()
- };
-
- if(!context.Users.Any(u =>u.UserName == user.UserName))
- {
- var passwordHasher = new PasswordHasher<ApplicatonUser>();
- var hased = passwordHasher.HashPassword(user,"");
- user.PasswordHash = hased;
- var userStore = new UserStore<ApplicationUser>(context);
- userStore.CreateAsync(user);
- }
- }
- catch(Exception ex)
- {
-
- }
- }
- }
- ```
-
- 模型
- ```
- public class RegisterViewModel
- {
- [Required]
- [EmailAddress]
- publci string Email{get;set;}
-
- [Requried]
- [DataType(DataType.Password)]
- public string Password{get;set;}
- }
-
- public class ChangePasswordViewModel
- {
- [Required]
- [DataType(DataType.EmailAddress)]
- public string Email{get;set;}
-
- [Required]
- [DataType(DataType.Password)]
- public string OldPassword{get;set;}
-
- [Requried]
- [DataType(DataType.Password)]
- public string NewPassword{get;set;}
- }
- ```
-
- 最后来到有关用户管理的控制器。
- ```
- [Route("api/[controller]")]
- publci class AccountController : ControllerBase
- {
- private readonly UserManager<ApplicationUser> _userManager;
- private readonly IUserClaimPrincipalFactory<ApplicatioinUser> _claimsFactory;
-
- //构造函数略
-
- [HttpPost("Register")]
- public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
- {
- if(ModelState.IsValid)
- {
- var user = new ApplicationUser {UserName = model.Email, Email = model.Email};
- var result = awati _userManager.CreateAsync(user, model.Password);
- return new JsonResult(result);
- }
- return BadRequest();
- }
-
- [HttpPost("ChangePassword")]
- [Authorize(AuthenticationSchemes = "Bearer")]
- public async Task<IActionResult> ChangePassword([FromBody]ChangePasswordViewModel model)
- {
- if(ModelState.IsValid)
- {
- var user = await _userManager.FindByEmailAsync(model.Email);
- if(user!=null)
- {
- var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
- return new JsonResult(result);
- }
- }
- return BadRequest();
- }
- }
- ```
|