代码改变世界

IdentityServer4授权和认证集成Identity和profile

2019-02-05 13:08  糯米粥  阅读(4497)  评论(3编辑  收藏  举报

 

identiyt的使用可以看之前的文章:https://www.cnblogs.com/nsky/p/10323415.html

之前的ids4授权服务器都是用的in-men方式把数据添加到内存,

现在应该改成identity对接数据库操作,因为之前的代码有,就不一一创建了

nuget包:IdentityServer4.AspNetIdentity

那么之前的TestUser以及TestUserStore都要替换掉

AddAspNetIdentity<ApplicationUser>()

那么Profile是什么? Profile就是用户资料,ids 4里面定义了一个IProfileService的接口用来获取用户的一些信息,

主要是为当前的认证上下文绑定claims。我们可以实现IProfileService从外部创建claim扩展到ids4里面。

定义一个ProfileServices类继承IProfileService

using AuthorizationServer.Models;
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace AuthorizationServer.Services
{
    /// <summary>
    ///  Profile就是用户资料,ids 4里面定义了一个IProfileService的接口用来获取用户的一些信息
    ///  ,主要是为当前的认证上下文绑定claims。我们可以实现IProfileService从外部创建claim扩展到ids4里面。
    ///  然后返回
    /// </summary>
    public class ProfileServices : IProfileService
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly RoleManager<ApplicationRole> _roleManager;
        public ProfileServices(
            UserManager<ApplicationUser> userManager,
            RoleManager<ApplicationRole> roleManager)
        {
            _userManager = userManager;
            _roleManager = roleManager;
        }

        public async Task<List<Claim>> GetClaimsFromUserAsync(ApplicationUser user)
        {
            var claims = new List<Claim> {
                new Claim(JwtClaimTypes.Subject,user.Id.ToString()),
                new Claim(JwtClaimTypes.PreferredUserName,user.UserName)
            };

            var role = await _userManager.GetRolesAsync(user);
            role.ToList().ForEach(f =>
            {
                claims.Add(new Claim(JwtClaimTypes.Role, f));
            });

            if(!string.IsNullOrEmpty(user.Avatar))
            {
                claims.Add(new Claim("avatar", user.Avatar));
            }
            claims.Add(new Claim("姓名", "tom"));
            return claims;
        }

        /// <summary>
        /// 获取用户Claims
        /// 用户请求userinfo endpoint时会触发该方法
        /// http://localhost:5003/connect/userinfo
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value;
            var user = await _userManager.FindByIdAsync(subjectId);
            context.IssuedClaims =await GetClaimsFromUserAsync(user);
        }

        /// <summary>
        /// 判断用户是否可用
        /// Identity Server会确定用户是否有效
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task IsActiveAsync(IsActiveContext context)
        {
            var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value;
            var user = await _userManager.FindByIdAsync(subjectId);
            context.IsActive = user != null; //该用户是否已经激活,可用,否则不能接受token

            /*
             这样还应该判断用户是否已经锁定,那么应该IsActive=false
             */
        }
    }
}

 

ConfigureServices也要修改:

这样的话。配置基本上就完成了

接下来用Hybrid Flow实现试试

因为CodeIdToken 有2种方式返回profile

ResponseType改成 code id_token

 options.ResponseType = OpenIdConnectResponseType.CodeIdToken;

//设置从UserInfoEndpoint获取claims信息

options.GetClaimsFromUserInfoEndpoint = true;

获取后映射到claims

 options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey("sub", "sub");
 options.ClaimActions.MapJsonKey("preferred_username", "preferred_username");
  options.ClaimActions.MapJsonKey("avatar", "avatar");
 options.ClaimActions.MapCustomJson("role", job => job["role"].ToString());

这样会返回的access_token信息也包含了该信息,

但如果claims过大,那么access_token也会很大

授权成功了。在view页面

引入:@using Microsoft.AspNetCore.Authentication

并获取

 

 

这样就会有ProfileServices.cs类中获取的clsims了

 

access_token中同样会有

 

 


id_token中是不会有的

上面说了,profile的cliams信息是通过access_token访问UserInfoEndpoint获取的

我们也可以通过postman试试

 

而这些信息都是从数据库读取出来的

 

 

 

 

 如果不设置GetClaimsFromUserInfoEndpoint=true

它默认是false

那么我们想得到怎么办,

1:可以自己用access_token去获取一次

2:授权服务器client设置 AlwaysIncludeUserClaimsInIdToken=true

这样就会把返回的profile信息包含在idtoken中,好像idtoken也会有很大的可能

好了。试试效果,这样其实idtoken和access_token都返回了profile信息