日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用Identity Server 4建立Authorization Server (6) - js(angular5) 客户端

發布時間:2023/12/4 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Identity Server 4建立Authorization Server (6) - js(angular5) 客户端 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

預備知識:?學習Identity Server 4的預備知識

第一部分:?使用Identity Server 4建立Authorization Server (1)

第二部分:?使用Identity Server 4建立Authorization Server (2)

第三部分:?使用Identity Server 4建立Authorization Server (3)

第四部分:?使用Identity Server 4建立Authorization Server (4)

第五部分:?使用Identity Server 4建立Authorization Server (5)

由于手頭目前用項目, 所以與前幾篇文章不同, 這次要講的js客戶端這部分是通過我剛剛開發的真是項目的代碼來講解的.

這是后端的代碼:?https://github.com/solenovex/asp.net-core-2.0-web-api-boilerplate

這里面有幾個dbcontext, 需要分別對Identity Server和Sales.DataContext進行update-database, 如果使用的是Package Manager Console的話.

進行update-database的時候, 如果是針對IdentityServer這個項目的要把IdentityServer設為啟動項目, 如果是針對Sales.DataContext的, 那么要把SalesApi.Web設為啟動項目, 然后再進行update-database.

項目結構如圖:

目前項目只用到AuthorizationServer和Sales這兩部分.

首先查看AuthorizationServer的相關配置: 打開Configuration/Config.cs

ApiResource:

public static IEnumerable<ApiResource> GetApiResources() { ? ? ? ?
?? ?
return new List<ApiResource>{ ? new ApiResource(CoreApiSettings.ApiResource.Name, CoreApiSettings.ApiResource.DisplayName) { }, ? ? ? ? ? ? ? ?new ApiResource(SalesApiSettings.ApiResource.Name, SalesApiSettings.ApiResource.DisplayName) {UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Email }}};}


紅色部分是相關代碼, 是所需要的ApiResource的定義.?

其中需要注意的是, 像user的name, email等這些claims按理說應該可以通過id_token傳遞給js客戶端, 也就是IdentityResource應該負責的. 但是我之所以這樣做是因為想把這些信息包含在access_token里面, 以便js可以使用包含這些信息的access_token去訪問web api, 這樣 web api就可以直接獲得到當前的用戶名(name), email了.?標準的做法應該是web api通過訪問authorization server的user profile節點來獲得用戶信息, 我這么做就是圖簡單而已.

所以我把這幾個claims添加到了ApiResource里面.?

配置好整個項目之后你可以把 name 去掉試試, 如果去掉的話, 在web api的controller里面就無法取得到user的name了, 因為js收到的access token里面沒有name這個claim, 所以js傳給web api的token里面也沒有name.?這個一定要自己修改下試試.

然后配置Client:

public static IEnumerable<Client> GetClients() { ? ? ?
? ? ?
return new List<Client>{ ? ? ? ? ? ? ? ?// Core JavaScript Clientnew Client{ClientId = CoreApiSettings.Client.ClientId,ClientName = CoreApiSettings.Client.ClientName,AllowedGrantTypes = GrantTypes.Implicit,AllowAccessTokensViaBrowser = true,RedirectUris = ? ? ? ? ? { CoreApiSettings.Client.RedirectUri, CoreApiSettings.Client.SilentRedirectUri },PostLogoutRedirectUris = { CoreApiSettings.Client.PostLogoutRedirectUris },AllowedCorsOrigins = ? ? { CoreApiSettings.Client.AllowedCorsOrigins },AllowedScopes ={IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,IdentityServerConstants.StandardScopes.Email,CoreApiSettings.ApiResource.Name}}, ? ? ? ? ? ? ? ?// Sales JavaScript Client new Client{ClientId = SalesApiSettings.Client.ClientId,ClientName = SalesApiSettings.Client.ClientName,AllowedGrantTypes = GrantTypes.Implicit,AllowAccessTokensViaBrowser = true,AccessTokenLifetime = 60 * 10,AllowOfflineAccess = true,RedirectUris = ? ? ? ? ? { SalesApiSettings.Client.RedirectUri, SalesApiSettings.Client.SilentRedirectUri },PostLogoutRedirectUris = { SalesApiSettings.Client.PostLogoutRedirectUris },AllowedCorsOrigins = ? ? { SalesApiSettings.Client.AllowedCorsOrigins },//AlwaysIncludeUserClaimsInIdToken = true,AllowedScopes ={IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,IdentityServerConstants.StandardScopes.Email,SalesApiSettings.ApiResource.Name,CoreApiSettings.ApiResource.Name}}};}

紅色部分是相關的代碼.

AccessTokenLifeTime是token的有效期, 單位是秒, 這里設置的是 10 分鐘.

AlwaysIncludeUserClaimsInIdToken默認是false, 如果寫true的話, 那么返回給客戶端的id_token里面就會有user的name, email等等user相關的claims信息.

然后是IdentityResource:

public static IEnumerable<IdentityResource> GetIdentityResources(){ ? ? ? ?
? ?
return new List<IdentityResource>{ ? ? ? ? ? ? ? ?new IdentityResources.OpenId(), ? ? ? ? ? ? ? ?new IdentityResources.Profile(), ? ? ? ? ? ? ? ?new IdentityResources.Email()};}

這里需要這三個IdentityResource, 其中的openId scope(identity resource)是必須要加上的, 如果沒有這個openid scope, 那么這個請求也許是一個合理的OAuth2.0請求, 但它肯定不會被當作OpenId Connect 請求.

如果你把profile這項去掉, 其他相關代碼也去掉profile, 那么客戶端新請求的id_token是無論如何也不會包括profile所包含的信息的(name等), 但是并不影響api resource里面包含相關的claim(access_token還是可以獲得到user的name等的).

其他的Identity Scopes(Identity Resource)所代表的內容請看文檔:?http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims:

profile:?name,?family_name,?given_name,?middle_name,?nickname,?preferred_username,profile,?picture,?website,?gender,?birthdate,?zoneinfo,?locale, and?updated_at.

email:?email?and?email_verified?Claims.

address:?address?Claim.

phone:?phone_number?and?phone_number_verified?Claims.

看一下Authorization Server的Startup.cs:

namespace AuthorizationServer { ? ?
? ?
public class Startup{ ? ? ?
? ? ? ?
public Startup(IConfiguration configuration){Configuration = configuration;} ? ? ? ?
? ? ? ?
? ? ? ?
public IConfiguration Configuration { get; } ? ? ? ?public void ConfigureServices(IServiceCollection services){ ? ? ? ?
? ? ? ?
var connectionString = Configuration.GetConnectionString("DefaultConnection"); ? ? ? ? ? ?var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer(connectionString));services.AddIdentity<ApplicationUser, IdentityRole>(options =>{ ? ? ? ? ? ? ? ?// Password settingsoptions.Password.RequireDigit = false;options.Password.RequiredLength = 4;options.Password.RequireNonAlphanumeric = false;options.Password.RequireUppercase = false;options.Password.RequireLowercase = false;options.Password.RequiredUniqueChars = 1; ? ? ? ? ? ? ? ?// Lockout settingsoptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);options.Lockout.MaxFailedAccessAttempts = 5;options.Lockout.AllowedForNewUsers = true; ? ? ? ? ? ? ? ?// Signin settingsoptions.SignIn.RequireConfirmedEmail = false;options.SignIn.RequireConfirmedPhoneNumber = false; ? ? ? ? ? ? ? ?// User settingsoptions.User.RequireUniqueEmail = false; ? ? ? ? ? ? ? ?}).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();services.ConfigureApplicationCookie(options =>{options.Cookie.Name = "MLHAuthorizationServerCookie";options.Cookie.HttpOnly = true;options.ExpireTimeSpan = TimeSpan.FromMinutes(60);options.LoginPath = "/Account/Login";options.LogoutPath = "/Account/Logout";options.AccessDeniedPath = "/Account/AccessDenied";options.SlidingExpiration = true;options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;});services.AddTransient<IEmailSender, EmailSender>();services.AddMvc();services.AddAutoMapper(); ? ? ? ? ? ?services.AddIdentityServer()#if DEBUG.AddDeveloperSigningCredential() #else.AddSigningCredential(new System.Security.Cryptography.X509Certificates.X509Certificate2(SharedSettings.Settings.AuthorizationServerSettings.Certificate.Path, SharedSettings.Settings.AuthorizationServerSettings.Certificate.Password)) #endif.AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddOperationalStore(options =>{options.ConfigureDbContext = builder =>builder.UseSqlServer(connectionString,sql => sql.MigrationsAssembly(migrationsAssembly));options.EnableTokenCleanup = true;options.TokenCleanupInterval = 30;}).AddAspNetIdentity<ApplicationUser>();services.AddAuthorization(options =>{options.AddPolicy(CoreApiAuthorizationPolicy.PolicyName, policy =>policy.RequireClaim(CoreApiAuthorizationPolicy.ClaimName, CoreApiAuthorizationPolicy.ClaimValue));});} ? ?
public void Configure(IApplicationBuilder app, IHostingEnvironment env){app.InitializeDatabase(); ?
? ? ? ? ?
if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();app.UseDatabaseErrorPage();} ? ? ? ? ?
? ? ? ? ? ?
else{app.UseExceptionHandler("/Home/Error");}app.UseStaticFiles();app.UseIdentityServer();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}} }

這里我只將Operation數據保存到了數據庫. 而Client和ApiResource, IdentityResource等定義還是放在了內存中, 我感覺這樣比較適合我.

Sales Web Api:

打開SalesApi.Web的Startup ConfigureServices: 這個非常簡單:

services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options =>{options.Authority = AuthorizationServerSettings.AuthorizationServerBase;options.RequireHttpsMetadata = false;options.ApiName = SalesApiSettings.ApiResource.Name;});

沒什么可說的.

js 客戶端 和?oidc-client.js

無論你使用什么樣的前端框架, 最后都使用oidc-client.js來和identity server 4來配套操作.?

我使用的是 angular 5:?由于這個代碼是公司的項目, 后端處于早期階段, 被我開源了, 沒什么問題.

但是前端是某機構買的一套收費的皮膚, 所以沒法開源, 這里我嘗試提供部分代碼, 我相信您一定可以從頭搭建出完整的js客戶端的.

我的前端應用流程是:

訪問前端地址, 如果沒有登錄用戶, 那么跳轉到Authorization Server進行登陸, 同意后, 返回到前端的網站.?

如果前端網站有登錄的用戶, 那么在用戶快過期的時候自動刷新token. 以免登陸過期.

前端應用訪問api時, 自動攔截所有請求, 把登陸用戶的access token添加到請求的authorization header, 然后再發送給 web api.

我把前端精簡了一下, 放到了網盤,是好用的

鏈接:?https://pan.baidu.com/s/1minARgc?密碼: ipyw

首先需要安裝angular-cli:

npm install -g @angular/cli

然后在項目根目錄執行:

npm install

雖然npm有點慢, 但是也不要使用cnpm, 有bug.

js客戶端參考

你可以參考官方文檔:?http://docs.identityserver.io/en/release/quickstarts/7_javascript_client.html

安裝oidc-client:

地址是:?https://github.com/IdentityModel/oidc-client-js, ?查看文檔的話點wiki即可.

在你的框架里面執行:

npm install oidc-client --save

配置oidc-client:

我的配置放在了angular5項目的environments里面, 因為這個配置根據環境的不同(開發和生產)里面的設定是不同的:

import { WebStorageStateStore } from 'oidc-client';
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `angular-cli.json`.export const environment = {production: false,authConfig: {authority: 'http://localhost:5000',client_id: 'sales',redirect_uri: 'http://localhost:4200/login-callback',response_type: 'id_token token',scope: 'openid profile salesapi email',post_logout_redirect_uri: 'http://localhost:4200',silent_redirect_uri: 'http://localhost:4200/silent-renew.html',automaticSilentRenew: true,accessTokenExpiringNotificationTime: 4, ? ? ? ?// silentRequestTimeout:10000,userStore: new WebStorageStateStore({ store: window.localStorage })},salesApiBase: 'http://localhost:5100/api/sales/',themeKey: 'MLHSalesApiClientThemeKeyForDevelopment'};

authority就是authorization server的地址.

redirect_url是登陸成功后跳轉回來的地址.

silent_redirect_uri是自動刷新token的回掉地址.

automaticSilentRenew為true是啟用自動安靜刷新token.

userStore默認是放在sessionStorage里面的, 我需要使用localStorage, 所以改了.

建立AuthService:

import { Injectable, EventEmitter } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { User, UserManager, Log } from 'oidc-client'; import 'rxjs/add/observable/fromPromise'; import { environment } from '../../../environments/environment';Log.logger = console; Log.level = Log.DEBUG;@Injectable() export class AuthService {private manager: UserManager = new UserManager(environment.authConfig);public loginStatusChanged: EventEmitter<User> = new EventEmitter();private userKey = `oidc.user:${environment.authConfig.authority}:${environment.authConfig.client_id}`;constructor(private router: Router) { ? ? ? ?this.manager.events.addAccessTokenExpired(() => {this.login();});}login() { ? ? ? ?this.manager.signinRedirect();}loginCallBack() { ? ? ? ?return Observable.create(observer => {Observable.fromPromise(this.manager.signinRedirectCallback()).subscribe((user: User) => { ? ? ? ? ? ? ? ? ? ?this.loginStatusChanged.emit(user);observer.next(user);observer.complete();});});}tryGetUser() { ? ? ? ?return Observable.fromPromise(this.manager.getUser());}logout() { ? ? ? ?this.manager.signoutRedirect();}get type(): string { ? ?
? ?
return 'Bearer';}get token(): string | null {const temp = localStorage.getItem(this.userKey); ? ?
? ?
if (temp) {const user: User = JSON.parse(temp); ? ? ? ?
? ?
return user.access_token;} ? ? ? ?return null;}get authorizationHeader(): string | null { ? ? ?
??
if (this.token) { ? ? ? ? ? ?return `${this.type} ${this.token}`;} ? ? ? ?return null;} }

UserManager就是oidc-client里面的東西. 我們主要是用它來操作.

constructor里面那個事件是表示, 如果用戶登錄已經失效了或者沒登錄, 那么自動調用login()登陸方法.

login()方法里面的signInRedirect()會直接跳轉到Authorization Server的登陸窗口.

logout()里的signoutRedirect()就會跳轉到AuthorizationServer并執行登出.

其中的userKey字符串是oidc-client在localStorage默認存放用戶信息的key, 這個可以通過oidc-client的配置來更改.

我沒有改, 所以key是這樣的: "oidc.user:http://localhost:5000:sales":

Token Interceptor 請求攔截器:

針對angular 5 所有的請求, 都應該加上authorization header, 其內容就是 access token, 所以token.interceptor.ts就是做這個工作的:

import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { User } from 'oidc-client'; import { environment } from '../../../environments/environment'; import { AuthService } from './auth.service';@Injectable() export class TokenInterceptor implements HttpInterceptor {constructor(private authService: AuthService) { }intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { ? ? ? ?const authHeader = this.authService.authorizationHeader;const authReq = req.clone({ headers: req.headers.set('Authorization', authHeader) }); ? ? ? ?return next.handle(authReq);} }

angular 5 的interceptor不會修改request, 所以只能clone.

設置AuthGuard:

angular5的authguard就是里面有個方法, 如果返回true就可以訪問這個路由, 否則就不可以訪問.

所以我在幾乎最外層添加了這個authguard, 里面的代碼是:

import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; import { Router } from '@angular/router'; import { User } from 'oidc-client'; import { AuthService } from './../services/auth.service'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map';@Injectable() export class AuthGuard implements CanActivate {constructor(private router: Router,private authService: AuthService) { }canActivate(): Observable<boolean> { ? ? ? ?return this.authService.tryGetUser().map((user: User) => {if (user) {return true;}this.authService.login();return false;});} }

意思就是, 取當前用戶, 如果有用戶那么就可以繼續訪問路由, 否走執行登陸動作.

所以訪問訪問網站后會跳轉到這, 這里有個內置用戶 admin 密碼也是admin, 可以使用它登陸.

外層路由代碼app-routing.module.ts:

import { NgModule } from '@angular/core'; import { Routes } from '@angular/router';import { AuthGuard } from './shared/guards/auth.guard';import { MainComponent } from './main/main.component'; import { LoginCallbackComponent } from './shared/components/login-callback/login-callback.component'; import { NotFoundComponent } from './shared/components/not-found/not-found.component';export const AppRoutes: Routes = [{path: '',redirectTo: 'dashboard',pathMatch: 'full', }, {path: 'login-callback',component: LoginCallbackComponent }, {path: '',component: MainComponent,canActivate: [AuthGuard],children: [{path: 'dashboard',loadChildren: './dashboard/dashboard.module#DashboardModule'}, {path: 'settings',loadChildren: './settings/settings.module#SettingsModule'}] }, { path: '**', component: NotFoundComponent }];


登陸成功后首先會跳轉到設置好的redirect_uri, 這里就是login-callback這個路由地址對應的component:


import { Component, OnInit } from '@angular/core'; import { AuthService } from '../../../shared/services/auth.service'; import { User } from 'oidc-client'; import { ToastrService } from 'ngx-toastr';@Component({selector: 'app-login-callback',templateUrl: './login-callback.component.html',styleUrls: ['./login-callback.component.css'] }) export class LoginCallbackComponent implements OnInit {constructor(private authService: AuthService,private toastr: ToastrService) { }ngOnInit() { ? ? ? ?this.authService.loginCallBack().subscribe((user: User) => {this.toastr.info('登陸成功, 跳轉中...', '登陸成功');if (user) {window.location.href = '/';}});}}


我在這里沒做什么, 就是重新加載了一下頁面, 我感覺這并不是好的做法.

您可以單獨建立一個簡單的頁面就像官方文檔那樣, 然后再跳轉到angular5項目里面.

這個頁面一閃而過:

回到angular5項目后就可以正常訪問api了.

自動刷新Token:

oidc-client的自動刷新token是只要配置好了, 你就不用再做什么操作了.

刷新的時候, 它好像是會在頁面上弄一個iframe, 然后在iframe里面操作.

不過還是需要建立一個頁面, 用于刷新:

<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title></head><body><h1 id="waiting">Waiting...</h1><div id="error"></div><script src="assets/js/oidc-client.min.js"></script><script>new Oidc.UserManager().signinSilentCallback(); ? ?</script></body></html>


很簡單就這些.

?

最后操作一下試試: 最好自己調試一下:

菜單那幾個都是好用的頁面.

相關文章:?

  • 基于OIDC(OpenID Connect)的SSO

  • 學習Identity Server 4的預備知識

  • 使用Identity Server 4建立Authorization Server (1)

  • 使用Identity Server 4建立Authorization Server (2)

  • 使用Identity Server 4建立Authorization Server (3)

  • 使用Identity Server 4建立Authorization Server (4)

  • 使用Identity Server 4建立Authorization Server (5)

  • IdentityServer4(10)- 添加對外部認證的支持之QQ登錄


原文:http://www.cnblogs.com/cgzl/p/7894446.html


.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com

總結

以上是生活随笔為你收集整理的使用Identity Server 4建立Authorization Server (6) - js(angular5) 客户端的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。