标签:convert stat cli format als 角色 contain 数据 net
本文转自:http://www.cnblogs.com/CreateMyself/p/4857799.html
上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话。
对于所谓的认证说到底就是安全问题,在Web API中有多种方式来实现安全,【accepted】方式来处理基于IIS的安全(通过上节提到的WindowsIdentity依赖于HttpContext和IIS认证)或者在Web API里通过使用Web API中的消息处理机制,但是如果我们想应用程序运行在IIS之外此时Windows Idenitity这一方式似乎就不太可能了,同时在Web API中本身就未提供如何处理认证的直接方式,我们不得不自定义来实现认证功能,同时这也是我们所推荐的方式,自己动手,丰衣足食。
通过上述图片的粗略信息我们可以看出在请求到Action方法之间要经过Web API消息处理管道,在请求到目标元素之前要经过HttpMessageHandler和认证过滤器,所以我们可以通过这两者来自定义实现认证。下面我们一一来看。
我们自定义一个认证身份(用户名和密码)的类,那么此类必须也就要继承于 GenericIdentity ,既然是基于基础验证,那么类型当然也就是Basic了。
1
2
3
4
5
6
7
8
9
|
public class BasicAuthenticationIdentity : GenericIdentity { public string Password { get ; set ; } public BasicAuthenticationIdentity( string name, string password) : base (name, "Basic" ) { this .Password = password; } } |
我们要自定义一个认证过滤器特性,并继承 AuthorizationFilterAttribute ,此时会变成如下:
1
2
3
4
5
|
public class BasicAuthenticationFilter : AuthorizationFilterAttribute { public override void OnAuthorization(HttpActionContext actionContext) {} } |
那么在这个重写的方法我们应该写什么呢?我们慢慢来分析!请往下看。
string authParameter = null;
var authValue = actionContext.Request.Headers.Authorization; //actionContext:Action方法请求上下文
if (authValue != null && authValue.Scheme == "Basic")
authParameter = authValue.Parameter; //authparameter:获取请求中经过Base64编码的(用户:密码)
if (string.IsNullOrEmpty(authParameter))
return null;
authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter)); //对编码的参数进行解码
var authToken = authParameter.Split(‘:‘); //解码后的参数格式为(用户名:密码)将其进行分割
if (authToken.Length < 2)
return null;
return new BasicAuthenticationIdentity(authToken[0], authToken[1]); //将分割的用户名和密码传递给此类构造函数进行初始化
public virtual BasicAuthenticationIdentity ParseHeader(HttpActionContext actionContext)
{
string authParameter = null;
var authValue = actionContext.Request.Headers.Authorization;
if (authValue != null && authValue.Scheme == "Basic")
authParameter = authValue.Parameter;
if (string.IsNullOrEmpty(authParameter))
return null;
authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));
var authToken = authParameter.Split(‘:‘);
if (authToken.Length < 2)
return null;
return new BasicAuthenticationIdentity(authToken[0], authToken[1]);
}
void Challenge(HttpActionContext actionContext)
{
var host = actionContext.Request.RequestUri.DnsSafeHost;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
}
public virtual bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext)
{
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(userPassword))
return false;
else
return true;
}
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
//下面是针对ASP.NET而设置
//if (HttpContext.Current != null)
// HttpContext.Current.User = principal;
一切已经就绪,此时在重写方法中进行相应的调用即可,如下:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthenticationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var userIdentity = ParseHeader(actionContext);
if (userIdentity == null)
{
Challenge(actionContext);
return;
}
if (!OnAuthorize(userIdentity.Name, userIdentity.Password, actionContext))
{
Challenge(actionContext);
return;
}
var principal = new GenericPrincipal(userIdentity, null);
Thread.CurrentPrincipal = principal;
base.OnAuthorization(actionContext);
}
自定义 CustomBasicAuthenticationFilter 并继承于 BasicAuthenticationFilter ,重写其虚方法。
public class CustomBasicAuthenticationFilter : BasicAuthenticationFilter
{
public override bool OnAuthorize(string userName, string userPassword, HttpActionContext actionContext)
{
if (userName == "xpy0928" && userPassword == "cnblogs")
return true;
else
return false;
}
}
注册自定义认证特性并进行调用
config.Filters.Add(new CustomBasicAuthenticationFilter());
[CustomBasicAuthenticationFilter]
public class ProductController : ApiController
{....}
至此对于其认证方式就已经完全实现,接下来我们通过【搜狗浏览器】来验收我们的成果。
看到如下认证其用户名和密码的图片,我们知道我们成功了一半
我们点击取消,观察是否返回401并添加质询头即WWW-Authenticate,如我们所料
我们输入正确的用户名和密码再试试看,结果认证成功,如下:
我们知道HttpMessageHandler是Web API中请求-响应中的消息处理管道的重要角色,但是真正实现管道串联的是DelegatingHandler,若你不懂Web API消息管道,请参考前面系列文章,所以我们可以自定义管道来进行拦截通过继承DelegatingHandler。下面我们一步步来实现基于此管道的认证。
和第一种方法一致不再叙述。
这一步当然是自定义管道进行处理并继承DelegatingHandler,重载在此类中的SendAsync方法,通过获得其请求并处理从而进行响应,若不懂此类中的具体实现,请参看前面系列文章。
public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage)
{
string authParameter = null;
var authValue = requestMessage.Headers.Authorization;
if (authValue != null && authValue.Scheme == "Basic")
authParameter = authValue.Parameter;
if (string.IsNullOrEmpty(authParameter))
return null;
authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));
var authToken = authParameter.Split(‘:‘);
if (authToken.Length < 2)
return null;
return new BasicAuthenticationIdentity(authToken[0], authToken[1]);
}
void Challenge(HttpRequestMessage request,HttpResponseMessage response)
{
var host = request.RequestUri.DnsSafeHost;
response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host));
}
public class BasicAuthenticationHandler : DelegatingHandler
{
private const string authenticationHeader = "WWW-Authenticate";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var crendentials = ParseHeader(request);
if (crendentials != null)
{
var identity = new BasicAuthenticationIdentity(crendentials.Name, crendentials.Password);
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
//针对于ASP.NET设置
//if (HttpContext.Current != null)
// HttpContext.Current.User = principal;
}
return base.SendAsync(request, cancellationToken).ContinueWith(task => {
var response = task.Result;
if (crendentials == null && response.StatusCode == HttpStatusCode.Unauthorized)
{
Challenge(request, response);
}
return response;
});
}
void Challenge(HttpRequestMessage request,HttpResponseMessage response)
{
var host = request.RequestUri.DnsSafeHost;
response.Headers.Add(authenticationHeader, string.Format("Basic realm=\"{0}\"", host));
}
public virtual BasicAuthenticationIdentity ParseHeader(HttpRequestMessage requestMessage)
{
string authParameter = null;
var authValue = requestMessage.Headers.Authorization;
if (authValue != null && authValue.Scheme == "Basic")
authParameter = authValue.Parameter;
if (string.IsNullOrEmpty(authParameter))
return null;
authParameter = Encoding.Default.GetString(Convert.FromBase64String(authParameter));
var authToken = authParameter.Split(‘:‘);
if (authToken.Length < 2)
return null;
return new BasicAuthenticationIdentity(authToken[0], authToken[1]);
}
}
上述我们自定义的BasicAuthenticationFilter此时就得继承 AuthorizeAttribute 该特性也是继承于上述的 AuthorizationFilterAttribute ,我们需要利用AuthorizeAttribute中的 IsAuthorized 方法来验证当前线程中的Principal是否已经被授权。
public class BasicAuthenticationFilter : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var identity = Thread.CurrentPrincipal.Identity;
if (identity != null && HttpContext.Current != null)
identity = HttpContext.Current.User.Identity;
if (identity != null && identity.IsAuthenticated)
{
var basicAuthIdentity = identity as BasicAuthenticationIdentity;
//可以添加其他需要的业务逻辑验证代码
if (basicAuthIdentity.Name == "xpy0928" && basicAuthIdentity.Password == "cnblogs")
{
return true;
}
}
return false;
}
}
通过 IsAuthorized 方法返回值来看,若为false,则返回401状态码,此时会触发 BasicAuthenticationHandler 中的质询,并且此方法里面主要是我们需要添加认证用户的业务逻辑代码。同时我们也说过我们第一种方法自定义实现的过滤器特性是 AuthorizationFilterAttribute (如果我们有更多逻辑使用这个特性是个不错的选择),而在这里是 AuthorizeAttribute (对于验证用户并且返回bool值使用此过滤器特性是个不错的选择)。
注册自定义管道以及认证过滤器特性
config.MessageHandlers.Add(new BasicAuthenticationHandler());
config.Filters.Add(new BasicAuthenticationFilter());
[BasicAuthenticationFilter]
public class ProductController : ApiController
{.....}
下面我们通过【360极速浏览器】来验收成果。点击按钮直接请求控制器
接下来取消,是否返回401
至此完美结束。
为了方便大家在移动端也能看到我分享的博文,现已注册个人公众号,扫描上方二维码即可,欢迎大家关注,有时间会及时分享相关技术博文。 感谢花时间阅读此篇文章,如果您觉得这篇文章你学到了东西也是为了犒劳下博主的码字不易不妨打赏一下吧,让楼主能喝上一杯咖啡,在此谢过了! 如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力! 本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/CreateMyself)/欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
[转]Web APi之认证(Authentication)两种实现方式【二】(十三)
标签:convert stat cli format als 角色 contain 数据 net
原文地址:http://www.cnblogs.com/freeliver54/p/6961859.html