IdentityServer4 简单使用,包括api访问控制,openid的授权登录,js访问
写在前面
先分享一首数摇:http://music.163.com/m/song?id=36089751&userid=52749763
其次是:对于identityServer理解并不是特别深刻,目前只是能简单的应用,里面一些具体设置以后可能慢慢更新到本文中。
最后:一张大图

IdentityServer4基于.net core的OAuth2.0和OpenId框架,主要应用于身份验证,单点登录,API访问控制等。。。
IdentityServer4 文档: https://identityserver4.readthedocs.io/en/release/
IdentityServer4 GitHub:https://github.com/IdentityServer/IdentityServer4/tree/dev/docs/topics
本文demo:https://github.com/aspros-luo/IdentityServer4Demo
api访问控制
一.首先需要创建授权中心,
新建.net core Web Application 项目,模板可以选择空或者web应用程序,偷懒的话直接选择web就好了
1.在project.json里添加 "IdentityServer4": "1.0.0",nuget添加一样可以。
2.我们需要新建一个配置文件configs.cs定义client和api作用域及账号信息具体代码如下:
public static IEnumerable<ApiResource> GeyApiResources()
{
return new List<ApiResource>
{
new ApiResource("UserApi","用户API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {"UserApi"}
},
new Client
{
ClientId = "ro.Client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {"UserApi"}
},
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "MVC",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002" }, AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UserApi"
},
AllowOfflineAccess = true
},
// JavaScript Client
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" }, AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UserApi"
},
}
};
}
public static List<TestUser> GeTestUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "",
Username = "qwerty",
Password = "a123"
},
new TestUser
{
SubjectId = "",
Username = "aspros",
Password = "b123"
}
};
}
3.在startup文件ConfigureServices里配置服务,Configure使用identityserver
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddIdentityServer()
.AddTemporarySigningCredential() .AddInMemoryApiResources(Configs.GeyApiResources())
.AddInMemoryClients(Configs.GetClients())
.AddTestUsers(Configs.GeTestUsers()); services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); app.UseApplicationInsightsRequestTelemetry(); if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseApplicationInsightsExceptionTelemetry(); app.UseStaticFiles(); //使用userIdentityServer
app.UseIdentityServer(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
4.更改当前应用程序的端口为:8000 (可略过)
以上,基本授权中心配置完毕
二.添加测试使用的api
新建.net core Web Application,模板使用Api
1.在project.json里添加 "IdentityServer4.AccessTokenValidation": "1.0.1", "Microsoft.AspNetCore.Cors": "1.1.0"(为跨域访问api做准备)
2.在Startup文件里添加跨域服务,配置授权中心地址及scope api作用域
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
#region 跨域
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:5003")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
#endregion
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//配置identityServer授权
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:8000",
AllowedScopes = { "UserApi" },
RequireHttpsMetadata = false
});
//跨域访问
app.UseCors("default"); app.UseMvc();
}
注释:cors可以暂时不配置,不影响后面调试
3.在controller上添加 [Authorize]
[Authorize]
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get()
{
//return Content("a");
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
4.更改api端口号,5001
以上测试使用Api暂时配置完毕
三.单元测试
新建.net core 类库
1.在project.json里添加
"dotnet-test-xunit": "2.2.0-preview2-build1029",
"IdentityModel": "2.1.1",
"xunit": "2.2.0-beta4-build3444",
"xunit.runner.console": "2.2.0-beta2-build3300"
具体如下:
{
"version": "1.0.0-*",
"testRunner": "xunit",
"dependencies": {
"dotnet-test-xunit": "2.2.0-preview2-build1029",
"IdentityModel": "2.1.1",
"xunit": "2.2.0-beta4-build3444",
"xunit.runner.console": "2.2.0-beta2-build3300"
},
"frameworks": {
"netcoreapp1.0.1": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.1"
}
}
}
}
}
2.新建测试类,UserClientTest,代码如下
public class UserClientTest
{
[Fact]
public async Task ClientApiTest()
{
//get access_token
var disco = await DiscoveryClient.GetAsync("http://localhost:8000");
var tokenClient = new TokenClient(disco.TokenEndpoint, "Client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("UserApi"); var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);//add bearer with access_token
var response = await client.GetAsync("http://localhost:5001/api/Values");//call API with access_token
var apiResult = response.Content.ReadAsStringAsync().Result;
Assert.NotEmpty(apiResult);
} [Fact]
public async Task PasswordApiTests()
{
var disco = await DiscoveryClient.GetAsync("http://localhost:8000");
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.Client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("qwerty", "a123", "UserApi"); var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);//add bearer with access_token
var response = await client.GetAsync("http://localhost:5001/api/Values");//call API with access_token
var apiResult = response.Content.ReadAsStringAsync().Result;
Assert.NotEmpty(apiResult);
} }
调试,
1请求授权中心,带入clientId,secret,scope作用域(api) 得到access_token
2得到access_token后,在header里添加Authorization:Bearer+access_token 请求api
3返回结果
OpenId 连接的mvc用户认证
一.授权中心更改的地方:
1.在授权中心里config里添加IdentityResource
public static IEnumerable<IdentityResource> GetyIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
2.config=》client里添加mvc
new Client
{
ClientId = "MVC",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002" }, AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UserApi"
},
AllowOfflineAccess = true
},
(AllowedScopes 设置授权范围,加上“UserApi” 及AllowOfflineAccess = true 后,在授权中心登录验证通过后,显示对应访问权限)
3.在startup里添加 AddInMemoryIdentityResources方法
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Configs.GeyApiResources())
.AddInMemoryClients(Configs.GetClients())
.AddInMemoryIdentityResources(Configs.GetyIdentityResources())
.AddTestUsers(Configs.GeTestUsers()); services.AddMvc();
}
(这里我出现过一个问题,在添加顺序的时候,将IdentityResources方法写在前面,单元测试请求api的时候会出现httpstatue 500错误)
4.添加UI
可以在github下载 https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/tree/release
(涉及到的页面及viewmodel较多。demo里我也重新整理过了)
以上,授权中心部分修改完毕
二.mvc客户端
添加.net core web application 选择web应用程序
1.在project.json 里添加
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.*",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.*",
"IdentityModel": "2.1.1"
2.在startup里添加UseCookieAuthentication,UseOpenIdConnectAuthentication,代码如下
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
}); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies", Authority = "http://localhost:8000",
RequireHttpsMetadata = false, ClientId = "MVC",
ClientSecret = "secret", ResponseType = "code id_token",
Scope = { "UserApi", "offline_access" },//添加权限请求项 GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true
}); app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
注意的地方,Authority为授权中心地址,ClientId与授权中心config里client里保持一致。Scope为请求权限项
3.在action上加上[Authorize],启用授权
4.更改端口为5002,可自行调节,需要与config保持一致
使用:
运行授权中心=》运行mvc客户端=》点击对应的授权的action链接=》跳转到授权中心登录页面=》输入账号密码后=》显示授权对应的权限列表=》跳回当前页面




以上。
js客户端访问
js端访问配置稍微麻烦点,因为中间出了一些问题,主要是前端js功力不够,看的的时候比较吃力
添加一个新项目。。先
在mvc端中,我们引用了一个库处理openid连接,在javascript中也需要引用一个类似的库
1.点击添加=》新建项=》左侧选择client-side选择NPM配置文件,默认为package.json
在package.json 里添加"oidc-client": "1.2.2",如下
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"oidc-client": "1.2.2"
}
}
找到oidc-client.js文件,将文件复制到wwwroot下(注意html页面引用就行)

2.添加两个html
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
</head>
<body>
<button id="login">Login</button>
<button id="api">Call API</button>
<button id="logout">Logout</button> <pre id="results"></pre>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="oidc-client.js"></script>
<script src="app.js"></script>
</body>
</html>
callBack.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script src="oidc-client.js"></script>
<script>
new Oidc.UserManager().signinRedirectCallback().then(function () {
window.location = "index.html";
}).catch(function (e) {
console.error(e);
});
</script>
</body>
</html>
3.添加app.js
/// <reference path="oidc-client.js" />
function log() {
document.getElementById('results').innerText = '';
Array.prototype.forEach.call(arguments, function (msg) {
if (msg instanceof Error) {
msg = "Error: " + msg.message;
}
else if (typeof msg !== 'string') {
msg = JSON.stringify(msg, null, 2);
}
document.getElementById('results').innerHTML += msg + '\r\n';
});
}
document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);
var config = {
authority: "http://localhost:8000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "id_token token",
scope: "openid profile UserApi",
post_logout_redirect_uri: "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
}
else {
log("User not logged in");
}
});
function login() {
mgr.signinRedirect();
}
function api() {
mgr.getUser().then(function (user) {
var url = "http://localhost:5001/api/Values";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
}
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
function logout() {
mgr.signoutRedirect();
}
4.将端口设为5003
运行授权中心=》运行javascriptclient=》点击login=》跳转到授权中心登录页面=》登录后,显示权限列表=》返回5003





以上
更新
看了昨天的评论,又去官方文档瞄了下,找到一些处理方法,如下
https://identityserver4.readthedocs.io/en/release/quickstarts/8_entity_framework.html
IdentityServer is designed for extensibility, and one of the extensibility points is the storage mechanism used for data that IdentityServer needs. This quickstart shows to how configure IdentityServer to use EntityFramework (EF) as the storage mechanism for this data (rather than using the in-memory implementations we had been using up until now).
大概意思是说,可以用数据库存储apiresour,client和identityserverresource 资源
新建或在原有的授权中心项目更改都是可以的,我这里是直接新建了一个
1.在project.json里 dependencies添加
"IdentityServer4.EntityFramework": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.1.0",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.1.*"
tools里添加(用于执行cmd的donet ef命令)
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
2.新建config文件
using System.Collections.Generic;
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test; namespace EntityFrameworkDemo
{
public class Config
{
public static IEnumerable<IdentityResource> GetyIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
public static IEnumerable<ApiResource> GeyApiResources()
{
return new List<ApiResource>
{
new ApiResource("UserApi","用户API"),
new ApiResource("api1","测试api")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {"UserApi"}
},
new Client
{
ClientId = "ro.Client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {"UserApi"}
},
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "MVC",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002" }, AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UserApi"
},
AllowOfflineAccess = true
},
// JavaScript Client
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" }, AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UserApi"
},
}
};
}
public static List<TestUser> GetTestUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "",
Username = "qwerty",
Password = "a123"
},
new TestUser
{
SubjectId = "",
Username = "aspros",
Password = "b123"
}
};
}
}
}
3.在startup里添加对应的服务
(1).修改ConfigureServices
旧:ConfigureServices里如下
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Configs.GeyApiResources())
.AddInMemoryClients(Configs.GetClients())
.AddInMemoryIdentityResources(Configs.GetyIdentityResources())
.AddTestUsers(Configs.GeTestUsers());
新:ConfigureServices里如下
var connectionString = @"server=.;database=IdentityServer4;trusted_connection=yes";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; // configure identity server with in-memory users, but EF stores for clients and resources
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddTestUsers(Config.GetTestUsers())
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)));
(2).修改Configure
app.UseIdentityServer();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = false
});
(3).初始化数据库
private void InitializeDatabase(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate(); var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate(); if (!context.Clients.Any())
{
foreach (var client in Config.GetClients().Where(client => !context.Clients.Any(c => c.ClientId == client.ClientId)))
{
context.Clients.Add(client.ToEntity());
}
} //context.SaveChanges(); if (!context.IdentityResources.Any())
{
foreach (
var identity in
Config.GetyIdentityResources()
.Where(identity => !context.IdentityResources.Any(i => i.Name == identity.Name)))
{
context.IdentityResources.Add(identity.ToEntity());
}
}
//context.SaveChanges();
if (!context.ApiResources.Any())
{
foreach (var api in Config.GeyApiResources().Where(api => !context.ApiResources.Any(a => a.Name == api.Name)))
{
context.ApiResources.Add(api.ToEntity());
}
} context.SaveChanges();
}
}
在Configure添加初始化数据库方法
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// this will do the initial DB population
InitializeDatabase(app);
...
}
4.在当前项目目录,如 G:\MyProject\IdentityServerDemo\src\EntityFrameworkDemo下shift+右键,在此处打开命令窗口,运行下面两行命令
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
5.重新运行授权中心前更改端口号为8001(如果是新建的项目),在sqlserver数据库会生成对应的数据库

6.将api的授权地址改成8001
7.运行单元测试
以上
如有问题,欢迎指正。
IdentityServer4 简单使用,包括api访问控制,openid的授权登录,js访问的更多相关文章
- 033.Kubernetes集群安全-API Server认证及授权
一 Kubernetes集群安全 1.1 安全机制 Kubernetes通过一系列机制来实现集群的安全控制,其中包括API Server的认证授权.准入控制机制及保护敏感信息的Secret机制等.集群 ...
- 使用 IdentityServer4 实现 OAuth 2.0 与 OpenID Connect 服务
IdentityServer4 是 ASP.NET Core 的一个包含 OIDC 和 OAuth 2.0 协议的框架.最近的关注点在 ABP 上,默认 ABP 也集成 IdentityServer4 ...
- IdentityServer4实现.Net Core API接口权限认证(快速入门)
什么是IdentityServer4 官方解释:IdentityServer4是基于ASP.NET Core实现的认证和授权框架,是对OpenID Connect和OAuth 2.0协议的实现. 通俗 ...
- Java得到一个整数的绝对值,不使用任何判断和比较语句,包括API.
/** * Java得到一个整数的绝对值,不使用任何判断和比较语句,包括API. <br> * 1.不得使用任何API,如Math.abs()等.<br> * 2.不得使用判断 ...
- 解决微信公众号授权登录和开放平台微信第三方应用授权登录获取到的用户Openid关联问题
开发背景: 最近一段时间一直在做关于微信方面的网站应用开发,这段时间也收获的不少关于微信开发方面的开发技能,接触的比较多的主要有微信公众号和微信网站app第三方登录授权,以及微信会员卡,优惠券和扫描二 ...
- Yii2 restful api创建,认证授权以及速率控制
Yii2 restful api创建,认证授权以及速率控制 下面是对restful从创建到速率控制的一个详细流程介绍,里面的步骤以及截图尽可能详细,熟悉restful的盆友可能觉得过于繁琐,新手不妨耐 ...
- IdentityServer4实现OAuth2.0四种模式之授权码模式
接上一篇:IdentityServer4实现OAuth2.0四种模式之隐藏模式 授权码模式隐藏码模式最大不同是授权码模式不直接返回token,而是先返回一个授权码,然后再根据这个授权码去请求token ...
- HTML5 file API加canvas实现图片前端JS压缩并上传
一.图片上传前端压缩的现实意义 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 这种体验包括两方面: 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅 ...
- 在dotnet core web api中支持CORS(跨域访问)
最近在写的Office add-in开发系列中,其中有一个比较共性的问题就是在add-in的客户端脚本中访问远程服务时,要特别注意跨域访问的问题. 关于CORS的一些基本知识,请参考维基百科的说明:h ...
随机推荐
- DragRow-GYF
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="DragRowDemo.as ...
- Selenium Chrome浏览器的启动以及proxy设置
Selenium Chrome浏览器的启动以及proxy设置 虽然WebDriver对Firefox的支持最好,之前写的脚本也都在Firefox浏览器运行,但最近项目做了整合,发现新整合的功能不太 ...
- angularJS中directive与directive 之间的通信
上一篇讲了directive与controller之间的通信:但是我们directive与directive之间的通信呢? 当我们两个directive嵌套使用的时候怎么保证子directive不会被 ...
- jQuery瀑布流插件——jQuery.Waterfall
插件--jQuery.Waterfall 思路: 其实只要了解了整个流程,要实现这个插件也不难,大家都玩过俄罗斯方块吧,原理差不多,找到合适的地方叠上去就好了,在这里,每个块的宽度是必需给定的,然后计 ...
- 2016年12月31日 星期六 --出埃及记 Exodus 21:26
2016年12月31日 星期六 --出埃及记 Exodus 21:26 "If a man hits a manservant or maidservant in the eye and d ...
- Keras
sudo pip install keras --安装 新建一个文件,里面存储的数据:第一列是属性,第二列是类别 11220044 011220044 011220044 011220033 1112 ...
- 【前端】stopPropagation, preventDefault, return false的区别
e.stopPropagation()阻止事件冒泡或者捕获 因为事件可以在各层级的节点中传递, 不管是冒泡还是捕获, 有时我们希望事件在特定节点执行完之后不再传递, 可以使用事件对象的 stopPro ...
- .NET蓝牙开源库:32feet.NET
在用C#调用蓝牙编程一文中我留个小悬念就是:InTheHand.Net.Personal.dll是怎么来的?这篇文章来解答这个问题,InTheHand.Net.Personal.dll就是来源于今天要 ...
- c语言简易文法
<程序>→<外部声明>|<程序><外部声明> <外部声明>→<函数定义>|<声明> <函数定义>→< ...
- 遗传算法在JobShop中的应用研究(part4:变异)
下面,我们以车间调度为例来谈谈遗传算法中的另一个重要操作变异.变异操作通常发生在交叉操作之后,它的操作对象是交叉得到的新染色体.在本文中我们通过随机交换染色体的两个位置上的值来得到变异后的染色体,变异 ...