.NET Core IdentityServer4实战 第Ⅴ章-单点登录
oidc可以说是oauth的改造版,在最初的oauth中,我们需要先请求一下认证服务器获取下access_token,然后根据access_token去get资源服务器, 况且oauth1 和 2 完全不兼容,易用性差,而oidc可以在登陆的时候就把信息返回给你,不需要你在请求一下资源服务器。下面我们根据oidc来做一个单点登录。
新建三个项目(.net core mvc)两个client(端口5001,5002),一个server(5000),首先在server中添加identityserver4的引用。
在server中config.cs用于模拟配置。
public class config { public static ienumerable<apiresource> getapiresource() { return new list<apiresource> { new apiresource("api","my api app") }; } public static ienumerable<client> getclients() { return new list<client> { new client() { clientid = "mvc", allowedgranttypes = granttypes.implicit, clientsecrets ={ new secret("secret".sha256()) }, requireconsent = false, redirecturis = {"http://localhost:5001/signin-oidc", "http://localhost:5002/signin-oidc" } , postlogoutredirecturis = {"http://localhost:5001/signout-callback-oidc" , "http://localhost:5002/signout-callback-oidc" }, allowedscopes = { identityserverconstants.standardscopes.profile, identityserverconstants.standardscopes.openid } } }; } public static list<testuser> gettestusers() { return new list<testuser> { new testuser() { subjectid = "10000", username = "zara", password = "112233" } }; } public static ienumerable<identityresource> getidentityresources() { return new list<identityresource> { new identityresources.openid(), new identityresources.profile(), new identityresources.email() }; } }
getclient方法中字段为redirecturis是登陆成功返回的地址,并且我们采用隐式模式(因为只是传统web中传递access_token),requireconsent是否出现同意授权页面,这个我们后续再细说.写完config.cs后,我们需要依赖注入到identityserver中。
public void configureservices(iservicecollection services) { services.configure<cookiepolicyoptions>(options => { // this lambda determines whether user consent for non-essential cookies is needed for a given request. options.checkconsentneeded = context => true; options.minimumsamesitepolicy = samesitemode.none; });
//config to identityserver services services.addidentityserver() .adddevelopersigningcredential() .addinmemoryclients(config.getclients()) .addtestusers(config.gettestusers()) .addinmemoryidentityresources(config.getidentityresources()) .addinmemoryapiresources(config.getapiresource()); services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1); }
在configure中添加代码 app.useidentityserver(); .我们还需要添加一个登陆页面,名为account.cshtml.
@{ viewdata["title"] = "index"; } <h2>index</h2> @using mvcwebfirstsolucation.models; @model loginvm; <div class="row"> <div class="col-md-4"> <section> <form method="post" asp-controller="account" asp-action="login" asp-route-returnurl="@viewdata["returnurl"]"> <h4>use a local to log in .</h4> <hr /> <div class="from-group"> <label asp-for="username"></label> <input asp-for="username" class="form-control"> <span asp-validation-for="username" class="text-danger"></span> </div> <div class="from-group"> <label asp-for="password"></label> <input asp-for="password" type="password" class="form-control"> <span asp-validation-for="username" class="text-danger"></span> </div> <div class="from-group"> <button type="submit" class="btn btn-default">log in </button> </div> </form> </section> </div> </div> @section scripts { @await html.partialasync("_validationscriptspartial") }
在控制器中我们写一个构造函数,用于将identityserver.text给我们封装好的对象传过来,这个对象是我们在config.cs中添加的用户信息,也就是getclients的返回值,全都在 testuserstore 中。其中还有一个提供好的方法,来给我们用,如果验证通过我们直接跳转到了传递过来的returnurl.
public class accountcontroller : controller { private readonly testuserstore _users; public accountcontroller(testuserstore ussotre) { _users = ussotre; } [httpget] [route("/account/login")] public iactionresult index(string returnurl = null) { viewdata["returnurl"] = returnurl; return view(); } private iactionresult redirettolocal(string returnurl) { if (url.islocalurl(returnurl)) { return redirect(returnurl); } return redirecttoaction(nameof(homecontroller.index),"home"); } [httppost] public async task<iactionresult> login(loginvm vm,string returnurl = null) { if (modelstate.isvalid) { viewdata["returnurl"] = returnurl; var user = _users.findbyusername(vm.username); if (user==null) { modelstate.addmodelerror(nameof(loginvm.username), "username is exists"); } else { if(_users.validatecredentials(vm.username, vm.password)) { var props = new authenticationproperties { ispersistent = true, expiresutc = datetimeoffset.utcnow.add(timespan.fromminutes(30)) }; await microsoft.aspnetcore.http .authenticationmanagerextensions .signinasync( httpcontext, user.subjectid, user.username, props ); return redirettolocal(returnurl); } modelstate.addmodelerror(nameof(loginvm.password), "wrong password"); } } return view(); } }
这个时候最基本的服务端已经配置成功了,我们开始配置受保护的客户端吧。
在客户端中我们不需要引入identityserver,因为我们只是去请求服务端然后看看cookies有没有在而已,所以我们只需要给受保护的客户端的api做好安全判断就好.
在受保护的控制器中添加 [authorize] 标识。然后再startup.cs中添加安全验证。并且在configure中use下 app.useauthentication();
public void configureservices(iservicecollection services) { services.addauthentication(options => { options.defaultscheme = "cookies"; options.defaultchallengescheme = "oidc"; }).addcookie("cookies").addopenidconnect("oidc", options => { options.signinscheme = "cookies"; options.authority = "http://localhost:5000"; options.requirehttpsmetadata = false; options.clientid = "mvc"; options.clientsecret = "secret"; options.savetokens = true; }); services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1); }
在首页中最好遍历下claims对象,这个是通过oidc直接给我们返回回来的.(最后另一个客户端也这么干!)
<div> @foreach (var claim in user.claims) { <dl> <dt>@claim.type</dt> <dd>@claim.value</dd> </dl> } </div>
现在我们启动项目看一下效果吧。
上一篇: 面试题【树:重建二叉树】
下一篇: 垃圾分类把程序员逼得用快递寄垃圾
推荐阅读
-
.NET Core IdentityServer4实战 第六章-Consent授权页
-
【.NET Core项目实战-统一认证平台】第九章 授权篇-使用Dapper持久化IdentityServer4
-
.NET Core IdentityServer4实战 第一章-入门与API添加客户端凭据
-
.NET Core IdentityServer4实战 第二章-OpenID Connect添加用户认证
-
.NET Core IdentityServer4实战 第Ⅴ章-单点登录
-
.NET Core IdentityServer4实战 第Ⅳ章-集成密码登陆模式
-
.NET Core IdentityServer4实战 第三章-使用EntityFramework Core进行持久化配置
-
【.NET Core项目实战-统一认证平台】第八章 授权篇-IdentityServer4源码分析
-
.NET Core IdentityServer4实战 第六章-Consent授权页
-
【.NET Core项目实战-统一认证平台】第九章 授权篇-使用Dapper持久化IdentityServer4