蹉跎岁月内蒙兵团,异世淘宝女王txt新浪,关于审理劳动争议案件的指导意见
现在随便一个网站,不用注册,只用微信扫一扫,然后就可以自动登录,然后第三方网站右上角还出现了你的微信头像和昵称,怎么做到的?
大概就这么个意思,oauth可以让第三方获取有限的授权去获取资源。
入门的看博客
英文好有基础的直接看协议
配置类:oauthoptions
处理器类: oauthhandler
public static class oauthextensions { public static authenticationbuilder addoauth(this authenticationbuilder builder, string authenticationscheme, action<oauthoptions> configureoptions) => builder.addoauth<oauthoptions, oauthhandler<oauthoptions>>(authenticationscheme, configureoptions); public static authenticationbuilder addoauth(this authenticationbuilder builder, string authenticationscheme, string displayname, action<oauthoptions> configureoptions) => builder.addoauth<oauthoptions, oauthhandler<oauthoptions>>(authenticationscheme, displayname, configureoptions); public static authenticationbuilder addoauth<toptions, thandler>(this authenticationbuilder builder, string authenticationscheme, action<toptions> configureoptions) where toptions : oauthoptions, new() where thandler : oauthhandler<toptions> => builder.addoauth<toptions, thandler>(authenticationscheme, oauthdefaults.displayname, configureoptions); public static authenticationbuilder addoauth<toptions, thandler>(this authenticationbuilder builder, string authenticationscheme, string displayname, action<toptions> configureoptions) where toptions : oauthoptions, new() where thandler : oauthhandler<toptions> { builder.services.tryaddenumerable(servicedescriptor.singleton<ipostconfigureoptions<toptions>, oauthpostconfigureoptions<toptions, thandler>>()); return builder.addremotescheme<toptions, thandler>(authenticationscheme, displayname, configureoptions); } }
下面是校验逻辑,这些配置是必需的。
public override void validate() { base.validate(); if (string.isnullorempty(clientid)) { throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(clientid)), nameof(clientid)); } if (string.isnullorempty(clientsecret)) { throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(clientsecret)), nameof(clientsecret)); } if (string.isnullorempty(authorizationendpoint)) { throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(authorizationendpoint)), nameof(authorizationendpoint)); } if (string.isnullorempty(tokenendpoint)) { throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(tokenendpoint)), nameof(tokenendpoint)); } if (!callbackpath.hasvalue) { throw new argumentexception(string.format(cultureinfo.currentculture, resources.exception_optionmustbeprovided, nameof(callbackpath)), nameof(callbackpath)); } }
public void postconfigure(string name, toptions options) { options.dataprotectionprovider = options.dataprotectionprovider ?? _dp; if (options.backchannel == null) { options.backchannel = new httpclient(options.backchannelhttphandler ?? new httpclienthandler()); options.backchannel.defaultrequestheaders.useragent.parseadd("microsoft asp.net core oauth handler"); options.backchannel.timeout = options.backchanneltimeout; options.backchannel.maxresponsecontentbuffersize = 1024 * 1024 * 10; // 10 mb } if (options.statedataformat == null) { var dataprotector = options.dataprotectionprovider.createprotector( typeof(thandler).fullname, name, "v1"); options.statedataformat = new propertiesdataformat(dataprotector); } }
这个statedataformat就是处理state字段的加密解密的,state在认证过程中用于防止跨站伪造攻击和存放一些状态信息,我们看一下协议的定义
state recommended. an opaque value used by the client to maintain state between the request and callback. the authorization server includes this value when redirecting the user-agent back to the client. the parameter should be used for preventing cross-site request forgery as described in section 10.12.
比如,认证之后的回跳地址就是存放在这里。所以如果希望从state字段中解密得到信息的话,就需要使用到propertiesdataformat。propertiesdataformat没有任何代码,继承自securedataformat。 为什么这里介绍这么多呢,因为实际项目中用到过这个。
public class securedataformat<tdata> : isecuredataformat<tdata> { private readonly idataserializer<tdata> _serializer; private readonly idataprotector _protector; public securedataformat(idataserializer<tdata> serializer, idataprotector protector) { _serializer = serializer; _protector = protector; } public string protect(tdata data) { return protect(data, purpose: null); } public string protect(tdata data, string purpose) { var userdata = _serializer.serialize(data); var protector = _protector; if (!string.isnullorempty(purpose)) { protector = protector.createprotector(purpose); } var protecteddata = protector.protect(userdata); return base64urltextencoder.encode(protecteddata); } public tdata unprotect(string protectedtext) { return unprotect(protectedtext, purpose: null); } public tdata unprotect(string protectedtext, string purpose) { try { if (protectedtext == null) { return default(tdata); } var protecteddata = base64urltextencoder.decode(protectedtext); if (protecteddata == null) { return default(tdata); } var protector = _protector; if (!string.isnullorempty(purpose)) { protector = protector.createprotector(purpose); } var userdata = protector.unprotect(protecteddata); if (userdata == null) { return default(tdata); } return _serializer.deserialize(userdata); } catch { // todo trace exception, but do not leak other information return default(tdata); } } }
addremoteschema和addshema的差别就是做了下面的处理,确认始终有不是远程schema的signinschema
private class ensuresigninscheme<toptions> : ipostconfigureoptions<toptions> where toptions : remoteauthenticationoptions { private readonly authenticationoptions _authoptions; public ensuresigninscheme(ioptions<authenticationoptions> authoptions) { _authoptions = authoptions.value; } public void postconfigure(string name, toptions options) { options.signinscheme = options.signinscheme ?? _authoptions.defaultsigninscheme ?? _authoptions.defaultscheme; } }
protected override async task<handlerequestresult> handleremoteauthenticateasync() { var query = request.query; var state = query["state"]; var properties = options.statedataformat.unprotect(state); if (properties == null) { return handlerequestresult.fail("the oauth state was missing or invalid."); } // oauth2 10.12 csrf if (!validatecorrelationid(properties)) { return handlerequestresult.fail("correlation failed.", properties); } var error = query["error"]; if (!stringvalues.isnullorempty(error)) { // note: access_denied errors are special protocol errors indicating the user didn't // approve the authorization demand requested by the remote authorization server. // since it's a frequent scenario (that is not caused by incorrect configuration), // denied errors are handled differently using handleaccessdeniederrorasync(). // visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. if (stringvalues.equals(error, "access_denied")) { return await handleaccessdeniederrorasync(properties); } var failuremessage = new stringbuilder(); failuremessage.append(error); var errordescription = query["error_description"]; if (!stringvalues.isnullorempty(errordescription)) { failuremessage.append(";description=").append(errordescription); } var erroruri = query["error_uri"]; if (!stringvalues.isnullorempty(erroruri)) { failuremessage.append(";uri=").append(erroruri); } return handlerequestresult.fail(failuremessage.tostring(), properties); } var code = query["code"]; if (stringvalues.isnullorempty(code)) { return handlerequestresult.fail("code was not found.", properties); } var tokens = await exchangecodeasync(code, buildredirecturi(options.callbackpath)); if (tokens.error != null) { return handlerequestresult.fail(tokens.error, properties); } if (string.isnullorempty(tokens.accesstoken)) { return handlerequestresult.fail("failed to retrieve access token.", properties); } var identity = new claimsidentity(claimsissuer); if (options.savetokens) { var authtokens = new list<authenticationtoken>(); authtokens.add(new authenticationtoken { name = "access_token", value = tokens.accesstoken }); if (!string.isnullorempty(tokens.refreshtoken)) { authtokens.add(new authenticationtoken { name = "refresh_token", value = tokens.refreshtoken }); } if (!string.isnullorempty(tokens.tokentype)) { authtokens.add(new authenticationtoken { name = "token_type", value = tokens.tokentype }); } if (!string.isnullorempty(tokens.expiresin)) { int value; if (int.tryparse(tokens.expiresin, numberstyles.integer, cultureinfo.invariantculture, out value)) { // https://www.w3.org/tr/xmlschema-2/#datetime // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx var expiresat = clock.utcnow + timespan.fromseconds(value); authtokens.add(new authenticationtoken { name = "expires_at", value = expiresat.tostring("o", cultureinfo.invariantculture) }); } } properties.storetokens(authtokens); } var ticket = await createticketasync(identity, properties, tokens); if (ticket != null) { return handlerequestresult.success(ticket); } else { return handlerequestresult.fail("failed to retrieve user information from remote server.", properties); } }
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
Blazor server side 自家的一些开源的, 实用型项目的进度之 CEF客户端
.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)
vue+.netcore可支持业务代码扩展的开发框架 VOL.Vue 2.0版本发布
网友评论