写在前面

先分享一首数摇: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访问的更多相关文章

  1. 033.Kubernetes集群安全-API Server认证及授权

    一 Kubernetes集群安全 1.1 安全机制 Kubernetes通过一系列机制来实现集群的安全控制,其中包括API Server的认证授权.准入控制机制及保护敏感信息的Secret机制等.集群 ...

  2. 使用 IdentityServer4 实现 OAuth 2.0 与 OpenID Connect 服务

    IdentityServer4 是 ASP.NET Core 的一个包含 OIDC 和 OAuth 2.0 协议的框架.最近的关注点在 ABP 上,默认 ABP 也集成 IdentityServer4 ...

  3. IdentityServer4实现.Net Core API接口权限认证(快速入门)

    什么是IdentityServer4 官方解释:IdentityServer4是基于ASP.NET Core实现的认证和授权框架,是对OpenID Connect和OAuth 2.0协议的实现. 通俗 ...

  4. Java得到一个整数的绝对值,不使用任何判断和比较语句,包括API.

    /** * Java得到一个整数的绝对值,不使用任何判断和比较语句,包括API. <br> * 1.不得使用任何API,如Math.abs()等.<br> * 2.不得使用判断 ...

  5. 解决微信公众号授权登录和开放平台微信第三方应用授权登录获取到的用户Openid关联问题

    开发背景: 最近一段时间一直在做关于微信方面的网站应用开发,这段时间也收获的不少关于微信开发方面的开发技能,接触的比较多的主要有微信公众号和微信网站app第三方登录授权,以及微信会员卡,优惠券和扫描二 ...

  6. Yii2 restful api创建,认证授权以及速率控制

    Yii2 restful api创建,认证授权以及速率控制 下面是对restful从创建到速率控制的一个详细流程介绍,里面的步骤以及截图尽可能详细,熟悉restful的盆友可能觉得过于繁琐,新手不妨耐 ...

  7. IdentityServer4实现OAuth2.0四种模式之授权码模式

    接上一篇:IdentityServer4实现OAuth2.0四种模式之隐藏模式 授权码模式隐藏码模式最大不同是授权码模式不直接返回token,而是先返回一个授权码,然后再根据这个授权码去请求token ...

  8. HTML5 file API加canvas实现图片前端JS压缩并上传

    一.图片上传前端压缩的现实意义 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 这种体验包括两方面: 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅 ...

  9. 在dotnet core web api中支持CORS(跨域访问)

    最近在写的Office add-in开发系列中,其中有一个比较共性的问题就是在add-in的客户端脚本中访问远程服务时,要特别注意跨域访问的问题. 关于CORS的一些基本知识,请参考维基百科的说明:h ...

随机推荐

  1. eclipse安装Veloeclipse

    step 1.Help-->install new software-->Add Name:Veloeclipse Value:http://veloeclipse.googlecode. ...

  2. JavaScript 中 map、foreach、reduce 间的区别

    一直对map.foreach.reduce这些函数很是生疏,今天看underscorejs时好好研究了一下,一研究我就更懵了,这不是一样嘛,都是遍历,所以我就去知乎找了一下,整理出了比较好的几个说法. ...

  3. android学习计划2

    1.linux下Kconfig编写规范 2.linux下Makefile编写规范 3.android下Makefile编写规范 4.android.mk编写规范 5.android系统裁剪

  4. Android 进程常驻----开机广播的简单守护以及总结

    这是一个轻量级的库,配置几行代码,就可以实现在Android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean master获取root权限下都无法杀死进程 支持系统2.3到 ...

  5. js 操作数组封装

    function OperateArray(array) { this.array = array; } OperateArray.prototype.hasValue = function(val) ...

  6. animation 动画重播

    <style> div, body{ margin: 0; padding: 0; } body{ background-color: #333; } @keyframes running ...

  7. 复旦高等代数 II(15级)思考题

    1.设 $f(x)=x^n+a_{n-1}x^{n-1}+\cdots+a_1x+a_0$ 是整系数首一多项式, 满足: $|a_0|$ 是素数且 $$|a_0|>1+\sum_{i=1}^{n ...

  8. VirtualBox 共享文件夾

    説明:host為window10,guest為centos7 一.安装VBoxLinuxAdditions 1. 在guest上挂载virtualbox安装目录下的VBoxGuestAdditions ...

  9. docker compose 笔记

    https://www.youtube.com/watch?v=Uez88TWOECg 是基于这个视频做的笔记. Docker Compose: Compose is a tool for defin ...

  10. Java时间的处理

    1. Java计算时间依靠1970年1月1日开始的毫秒数.2. Date类的构造函数Date()返回代表当前创建的时刻的对象.Date的方法getTime()返回一个long值在数值上等于1970年1 ...