angularjs+webapi2 跨域Basic 认证授权(二)
在上一篇中大概演示了 整个认证授权的过程。今天在这篇博客中将结合上一篇的例子继续在跨域的情况
我们用ionic 写一个简单的页面
值得注意的是 在ionic.bundle.js 里面集成了angularjs 所以不用额外去引用angularjs 文件
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <link href="ionic-v1.3.0/css/ionic.min.css" rel="stylesheet" /> <script src="ionic-v1.3.0/js/ionic.bundle.min.js"></script> </head> <body ng-app="basicApp" ng-controller="app.login as vm"> <ion-content> <ion-list ng-repeat="data in vm.datas"> <ion-item> {{data}} </ion-item> </ion-list> </ion-content> </body> </html>
相关的js文件
<script type="text/javascript"> (function () { var app=angular.module('basicApp', ['ionic']); app .controller('app.login', ['$http',function ($http) { var vm = this; vm.datas = []; vm.getValue = function () { $http({ url: "http://localhost:1894/api/basic/get", method: 'GET'
}).success(function (datas) { vm.datas = datas; }).error(function (data,status) { if (status == '401') { } }); } vm.getValue(); }]); })(); </script>
运行看一下效果,不出意外的话
在web.config 里面添加如下配置
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept, authorization" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE,OPTIONS" /> <add name="Access-Control-Allow-Origin" value="*"/> </customHeaders> </httpProtocol> </system.webServer>
这里的配置有些地方是后面需要用到的,为了减少篇幅,这里一并贴出来了。
配置完成之后 刷新页面,不出意外的话
因为在我们这个跨域请求header 里面 并没有Authorization 相关信息,在上一篇浏览器登录之后的信息里面可以看到
$http({ url: "http://localhost:1894/api/basic/get", method: 'GET', headers:{'Authorization':'Basic dHp5OjEyMw=='} }).success(function (datas) { vm.datas = datas; }).error(function (data,status) { if (status == '401') { //局部处理的方式 //document.getElementById('logindiv').style.display = "block"; } });
在代码中加入红色部分 'Basic dHp5OjEyMw==' (为了理解这个过程这里是用的之前浏览器生成的那个header 里面的authorization信息)
然后再看看效果
然后需要注意的是
这里有一个options 请求:这是在跨域的情况下浏览器会发送一个试探请求(具体的百度一下)
所以我们在webapi 的get action 上面要加上 HttpOptions (这里跟之前说的webconfig配置也有关系)
不然的话在进行options请求的时候会返回405错误,导致整个请求没法进行
[HttpBasicAuthorize] public class BasicController : ApiController { [HttpOptions] [HttpGet] public IEnumerable<string> Get() { return new string[] { "tzy","123"}; } }
好了,到这里呢 这个ajax 跨域请求的过程也模拟完成。不过很明显还差了些东西,我们接下来就通过自己的登陆界面和方式来替换刚才给定的header 里面的 authorization 参数:
1,建一个登陆页面(为了好理解,还是在刚才那个页面上吧)
<ion-content> <div class="list" id="logindiv" style="display:none"> <label class="item item-input"> <input type="text" placeholder="用户名" ng-model="vm.user.name" /> </label> <label class="item item-input"> <input type="text" placeholder="密码" ng-model="vm.user.password" /> </label> <label> <button class="button button-block button-balanced" ng-click="vm.loginSubmit(vm.user)"> <i class="ion ion-android-add"></i> 登录 </button> </label> </div> <ion-list ng-repeat="data in vm.datas"> <ion-item> {{data}} </ion-item> </ion-list> </ion-content>
在之前的代码中插入了 一个 id=logindiv 的内容 默认是隐藏的
然后实现我们的login api
[AllowAnonymous] [HttpOptions] [HttpPost] public LoginResult Login(UserDto user) { if (user != null) { if (user.Name.Equals("tzy") && user.Password.Equals("123")) { // return new HttpResponseMessage( string s = Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(String.Format("{0}:{1}", user.Name, user.Password))); return new LoginResult() { Success = true, Token = s }; } } return new LoginResult() { Success = false, Token = null }; }
这里贴一下 UserDto 和 LoginResult类
public class LoginResult { public bool Success { get; set; } public string Token { get; set; } } public class UserDto { public string Name { get; set; } public string Password { get; set; } }
[AllowAnonymous] 就是允许未验证访问
string s = Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(String.Format("{0}:{1}", user.Name, user.Password))); 这句代码就体现了基础认证加密实现 ,这也就是为什么说basic认证是不安全的,但这不是今天要讨论的问题,我们重在理解这个过程 ,至于加密方式 在过程的基础上都可以加强。
然后再实现一下 客户端ajax 请求登录的代码
vm.loginSubmit = function (user) { console.log(user); $http({ url: "http://localhost:1894/api/basic/login", method:'POST', data: user }).success(function (result) { if (result.Success) { // localStorage.token = result.Token; localStorage.setItem('token', result.Token); console.log(localStorage.getItem('token')); vm.getValue(); document.getElementById('logindiv').style.display = "none"; } }); }
我们用 localStorage 把收到的 token 存储起来
然后把之前的 getValue 更改一下
vm.getValue = function () { $http({ url: "http://localhost:1894/api/basic/get", method: 'GET', headers: { 'Authorization': 'Basic ' + localStorage.getItem('token') } }).success(function (datas) { vm.datas = datas; }).error(function (data,status) { if (status == '401') { //如果认证没有通过 则显示登陆界面 document.getElementById('logindiv').style.display = "block"; } }); }
改动有2点:1 headers authorization 的值 从localStorage中取
2.在每次请求 如果401 认证失败 就显示登录界面
然后让我们看一下效果吧:
这个gif 能很清楚的看到这个过程 -》在第一次请求getValue 的时候 报了一个401 错误然后登陆之后拿到token信息(其实就是认证信息),然后再次请求成功。
后续可能会利用angularjs 的拦截器机制 统一拦截处理 httpstatus 问题。