概述

本文使用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. [Qt] 去掉QMessageBox标题栏上的图标

    msgBox.setWindowFlags(Qt::Drawer);

  2. scala教程之:可见性规则

    文章目录 public Protected private scoped private 和 scoped protected 和java很类似,scala也有自己的可见性规则,不同的是scala只有 ...

  3. Hardware Introduction

    计算机硬件组成可以概括为下图: CPU CPU生产商主要是Intel和AMD. Intel的产品主要有四种: Celeron(赛扬):低端处理器 Pentium(奔腾):比赛扬强,比酷睿弱 Xeon( ...

  4. 数据库SQL语言从入门到精通--Part 4--SQL语言中的模式、基本表、视图

    数据库从入门到精通合集(超详细,学习数据库必看) 前言: 使用SQL语言时,要注意SQL语言对大小写并不敏感,一般使用大写.所有符号一定是西文标点符号(虽然是常识,但我还是提一嘴) 1.模式的定义与删 ...

  5. 疯子的算法总结(七) 字符串算法之 manacher 算法 O(N)解决回文串

    有点像DP的思想,写写就会做. #include<bits/stdc++.h> using namespace std; const int maxn=1e7+5; char a[maxn ...

  6. Linux安装maven(详细教程)

    一.简介 Maven是意第绪语,意思是“知识的积累者”,最初是为了简化Jakarta Turbine项目中的构建过程.有几个项目,每个项目都有自己的Ant构建文件,所有项目都略有不同.JAR已检入CV ...

  7. CentOS安装jsoncpp

    两种安装方式: 通过cmake安装 通过scons安装 cmake安装见cmake安装jsoncpp,scons安装见下文. 1. 安装scons .tar.gz export MYSCONS=/ro ...

  8. 解决json字符串转为对象时LocalDateTime异常问题

    1 出现异常 这次的异常出现在前端向后端发送请求体里带了两个日期,在后端的实体类中,这两个日期的格式都是JDK8中的时间类LocalDateTime.默认情况下,LocalDateTime只能解析20 ...

  9. K - Painful Bases 状压dp

    Painful Bases LightOJ - 1021 这个题目一开始看,感觉有点像数位dp,但是因为是最多有16进制,因为限制了每一个数字都不同最多就有16个数. 所以可以用状压dp,看网上题解是 ...

  10. mybatis添加信息自动生成主键

    一.使用Oracle数据库 举例:添加员工的时候自动生成主键 1.在dao接口中声明方法 2.在mapper中实现该方法 需要先在数据表中创建序列 3.测试 注意:在调用过save方法之后,emp对象 ...