概述

本文使用ionic/angular开发网页前台,asp.net core webapi开发restful service,使用keycloak保护前台页面和后台服务,并且利用open api自动生成代码功能,减少了重复代码编写。

准备工作

1、使用docker搭建并启动keycloak服务器,新建名称为test的realm,并建立几个测试用户,并且建立1个名称为my_client的客户端,注意客户端的回调url要正确。

2、安装ionic,使用 ionic start myApp tabs,初始化一个tabs格式的前端应用。

3、使用dotnet new webapi命令创建一个webapi。

WebApi设置

1、控制器使用[Authorize]保护

namespace WebApi1.Controllers
{
/// <summary>
/// 天气预报服务
/// </summary>
[Authorize]
[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
}; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
} /// <summary>
/// 获取全部天气预报信息
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(, ).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-, ),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}

2、修改项目文件

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
<TargetFramework>netcoreapp3.</TargetFramework>
</PropertyGroup> <PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);</NoWarn>
</PropertyGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.4.1" />
</ItemGroup> </Project>

1591那一段主要是为了编译时生成xml格式的注释文档,该文档给OpenApi使用,用来给方法和属性添加注释。

JwtBearer用于实现基于JWT的身份认证,Swashbuckle.AspNetCore用于自动生成OpenApi文档以及图形界面。

3、修改Startup

 namespace WebApi1
{
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:8100").AllowAnyHeader().AllowAnyMethod();
});
}); services.AddControllers(); services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "一个测试用的天气预报服务", Version = "v1" }); var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
}); services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "http://localhost:8180/auth/realms/test";
options.RequireHttpsMetadata = false;
options.Audience = "account";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "preferred_username"
};
});
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseHttpsRedirection(); // Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(); // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.RoutePrefix = string.Empty;
}); app.UseRouting(); app.UseAuthentication();
app.UseAuthorization(); app.UseCors(MyAllowSpecificOrigins); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

17行代码添加CORS支持,此处只允许来自我的客户端的访问。

28行配置OpenApi文档生成逻辑。

68行生成OpenApi文档界面,使用c.RoutePrefix使得一打开网站就能看到文档界面,而不是打开404.

37行配置JWT参数,连接到keycloak服务的test realm。

4、修改侦听端口

为了方便配置回调接口,在lauchSettings.json中将侦听地址改为http://localhost:5000

5、dotnet run启动

使用浏览器打开http://localhost:5000,看到如下文档界面。

ionic配置keycloak支持

使用keyclock-angular快速添加对于keyclock的支持,https://github.com/mauriciovigolo/keycloak-angular

npm i --save keycloak-angular

npm i --save keycloak-js@version

这里的version我设置的是9.0.3,最新的是10,但是keyclock-angular安装时明确指定要求版本小于10,不知道是不是一个bug。

安装完毕后,修改app.module.ts,

 import { BASE_PATH } from './../../services/variables';
import { NgModule, APP_INITIALIZER} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
import { HttpClientModule } from '@angular/common/http'; @NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, KeycloakAngularModule, HttpClientModule],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
{
provide: APP_INITIALIZER,
useFactory: initializer,
multi: true,
deps: [KeycloakService]
},
{
provide: BASE_PATH, useValue: 'http://localhost:5000'
}
],
bootstrap: [AppComponent]
}) export class AppModule {} function initializer(keycloak: KeycloakService): () => Promise<any> {
return (): Promise<any> => {
return new Promise(async (resolve, reject) => {
try {
await keycloak.init({
config: {
url: 'http://localhost:8180/auth',
realm: 'test',
clientId: 'my-client'
},
initOptions: {
onLoad: 'login-required',
checkLoginIframe: false
},
bearerExcludedUrls: []
});
resolve();
} catch (error) {
reject(error);
}
});
};
}

首先,第2行增加引入APP_INITIALIZER;

然后,12行引入keyclock相关组件;

然后,23行增加provider,其实就是指定程序启动时执行的脚本为initializer;

最后,38行编写initializer方法,配置keyclock启动参数,并且配置了应用启动时直接调用登录(可选)

接下来,实现如下的CanAuthenticationGuard,用来控制路由。

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular'; @Injectable({
providedIn: 'root'
})
export class CanAuthenticationGuard extends KeycloakAuthGuard implements CanActivate {
constructor(protected router: Router, protected keycloakAngular: KeycloakService) {
super(router, keycloakAngular);
} isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise((resolve, reject) => {
if (!this.authenticated) {
this.keycloakAngular.login()
.catch(e => console.error(e));
return reject(false);
} const requiredRoles: string[] = route.data.roles;
if (!requiredRoles || requiredRoles.length === 0) {
return resolve(true);
} else {
if (!this.roles || this.roles.length === 0) {
resolve(false);
}
resolve(requiredRoles.every(role => this.roles.indexOf(role) > -1));
}
});
}
}

在app-routing.module.ts中引用该guard,并且在需要控制的路由如下处理

const routes: Routes = [
{
path: '',
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
},
{
path: 'hero/list',
canActivate: [CanAuthenticationGuard],
loadChildren: () => import('./hero/list/list.module').then( m => m.ListPageModule)
},

主要是canActive一行。

配置keyclock

略去如何安装以及运行keyclock的详细说明,我是通过docker安装运行的。

首先,新建realm = test

然后,新建client = my-client,url = http://localhost:8100,也就是ionic app调试运行的地址

然后,建立一个用户并配置密码。

浏览器打开http://localhost:8100,如果配置正确,会看到请求被重定向到keycloak的登陆界面,输入用户名密码后跳转回ionic app。

至此,inoic集成keycloak的工作基本完成。

特别要说明的,集成keycloak之后,不仅能控制路由的访问,而且所有的http请求都会自动加上登陆时获取的token,方便了webapi的调用。你可以在app.module.ts的52行,添加例外。

调用WebApi

如果不使用OpenApi代码自动生成,调用WebApi的套路无非是编写interface,然后编写service,使用http调用webapi,有许多重复的代码和机械步骤。

OpenApi代码生成器解决了这一问题,可以替代我们生成这些代码,很方便。

首先,安装全局工具 https://github.com/OpenAPITools/openapi-generator

该工具提供了npm包,但只是一个封装,还是需要系统有java环境的。

npm install @openapitools/openapi-generator-cli -g

使用如下命令生成代码:

openapi-generator -i {swagger文件url} -g typescript-angular -o {代码存放目录}

运行完毕后,看到代码目录下生成了一堆文件,暂时不必理会这些文件,也不要修改这些文件。

找到任意一个page的ts文件,添加代码,使用生成的客户端:

import { WeatherForecast } from './../../../../services/model/weatherForecast';
import { WeatherForecastService } from './../../../../services/api/weatherForecast.service';
import { Component, OnInit } from '@angular/core'; @Component({
selector: 'app-list',
templateUrl: './list.page.html',
styleUrls: ['./list.page.scss'],
})
export class ListPage implements OnInit {
weathers: WeatherForecast[]; constructor(private weatherforcastService: WeatherForecastService) { } ngOnInit() {
this.weatherforcastService.weatherForecastGet()
.subscribe(data => this.weathers = data);
} }

代码相当简单,WeatherForcast和WeatherForecastService已经帮我们自动生成了,直接使用就可以,是不是很cool呢?!

接下来,你可能有疑问了,光看到service,也不能修改生成的源码,那么去哪儿修改service的地址呢?很简单,翻看前面的app.module.ts,第1行引入BASE_PATH,然后在provider中替换它的内容即可。

总结

至此,我们实现了在angular/ionic中使用keycloak进行oauth认证并且访问webapi资源,还实现了使用openapi代码生成器自动生成客户端代码。

ionic + asp.net core webapi + keycloak实现前后端用户认证和自动生成客户端代码的更多相关文章

  1. .NetCore2.1 WebAPI 根据swagger.json自动生成客户端代码

    前言 上一篇博客中我们可以得知通过Swagger插件可以很方便的提供给接口开发者在线调试,但是实际上Swagger附带的功能还有很多, 比如使用NSwag生成客户端调用代码,进一步解放接口开发者. N ...

  2. dotnet core webapi +vue 搭建前后端完全分离web架构

    架构 服务端采用 dotnet core  webapi 前端采用: Vue + router +elementUI+axios 问题 使用前后端完全分离的架构,首先遇到的问题肯定是跨域访问.前后端可 ...

  3. dotnet core webapi +vue 搭建前后端完全分离web架构(一)

    架构 服务端采用 dotnet core  webapi 前端采用: Vue + router +elementUI+axios 问题 使用前后端完全分离的架构,首先遇到的问题肯定是跨域访问.前后端可 ...

  4. 开源项目之ASP.NET Core + Vue.js 的前后端分离的通用后台管理系统框架

    年前看了这个开源项目感觉很不错,这个小项目对于传统的.net 开发人员,想做技术提升是一个很不错的参考案例. 开源项目演示地址:https://dnczeus.codedefault.com/logi ...

  5. Keycloak & Asp.net core webapi 整合跳坑之旅

    前言 之前,一直使用IdentityServer4作为.net core程序的外部身份认证程序,ID4的优点自不必说了,缺点就是缺乏完善的管理界面. 后来,学习java quarkus框架时,偶然遇到 ...

  6. Asp.net Core WebApi 使用Swagger做帮助文档,并且自定义Swagger的UI

    WebApi写好之后,在线帮助文档以及能够在线调试的工具是专业化的表现,而Swagger毫无疑问是做Docs的最佳工具,自动生成每个Controller的接口说明,自动将参数解析成json,并且能够在 ...

  7. Asp.net core WebApi 使用Swagger生成帮助页

    最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...

  8. Asp.net core WebApi 使用Swagger生成帮助页实例

    最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...

  9. 使用xunit对asp.net core webapi进行集成测试

    新项目我们采用前后端分离,后端采用asp.net core webapi, 如何对后端代码进行自动化测试呢,有以下几种方案: 1. 单元测试,目前这个方案对我们来说难度很大,抛开时间的问题,单元测试对 ...

随机推荐

  1. Spring Boot devtool的使用

    文章目录 添加Spring Boot devtool依赖 默认属性 自动重启 Live Reload 全局配置 Spring Boot devtool的使用 Spring Boot为我们提供了一个便捷 ...

  2. (原創) 如何在Nios II顯示8位數的七段顯示器? (SOC) (Nios II) (SOPC Builder) (DE2-70)

    Abstract本文討論如何在Nios II控制8位數的七段顯示器. Introduction使用環境:Quartus II 8.0 + Nios II EDS 8.0 + DE2-70 (Cyclo ...

  3. CF思维联系– Codeforces-989C C. A Mist of Florescence

    ACM思维题训练集合 C. A Mist of Florescence time limit per test 1 second memory limit per test 256 megabytes ...

  4. 虚拟 IP 设为静态 IP

    一:虚拟机设置桥接模式 1.进入虚拟机设置中将网络适配器设置成桥接模式 2.编辑--虚拟网络编辑器--选择桥接 二:将虚拟IP设置成静态IP (1)方案一:进入虚拟机系统 System 设置 (2)方 ...

  5. nginx的数据结构集合(随时更新)

    在学习nginx的时候,因为其数据结构略多,看过后一般就忘记了.所以边学习边记录在这里吧,方便以后查看. ngx_buf_t:缓冲区结点 1: typedef struct ngx_buf_s ngx ...

  6. docker overlay network

    下载binary consul wget https://releases.hashicorp.com/consul/1.2.2/consul_1.2.2_linux_amd64.zip unzip ...

  7. web安全笔记

    判断网站是否伪静态 把.html后缀即可或者加.php?id=99 例子:33_99.html   去掉.html和_变成33.php?id=99 即可判断是否伪静态 判断网站是否在win或者linu ...

  8. Servlet --启动时创建、配置url、ServlectContext、初始化参数、获取资源

    servlet的版本的区别 2.5版本, Servlet的配置只支持在xml文件中的配置 3.0版本: Servlet的配置支持在xml文件中的配置, 也可以使用注解的方式, 默认使用注解 让服务器在 ...

  9. SpringBoot:模板引擎 thymeleaf、ContentNegotiatingViewResolver、格式化转换器

    目录 模板引擎 thymeleaf ContentNegotiatingViewResolver 格式化转换器 模板引擎 thymeleaf.ContentNegotiatingViewResolve ...

  10. java读源码 之 list源码分析(LinkedList)

    文章目录 LinkedList: 继承关系分析: 字段分析: 构造函数分析: 方法分析: LinkedList: 继承关系分析: public class LinkedList<E> ex ...