在ASP.NET Core中使用Angular2,以及与Angular2的Token base身份认证

Angular2是对Angular1的一次彻底的,破坏性的更新。

相对于Angular1.x,借用某果的广告语,唯一的不同,就是处处都不同。

  • 首先,推荐的语言已经不再是Javascript,取而代之的TypeScript,(TypeScript = ES6 + 类型系统 + 类型注解), TypeScriipt的类型系统对于开发复杂的单页Web app大有帮助,同时编译成javascript后的执行效率也比大多数手写javascript要快。有兴趣的同学可以查阅官方文档:英文传送门 | 中文传送门。

  • 得益于彻底重构,性能相对于Angular1.x有了大幅提升,也更适合再全平台部署。

  • Angular2是基于Component的,Component可以理解为是1.x时代的Controller + $Scope + view

  • View的很多语法也做了更新,比如<li ng-repeat="movie in vm.movies"></li> 变成了 <li *ngFor="let movie of movies"></li>

  • 等等。。

关于Angular2,强烈建议查阅官方文档:英文传送门 | 中文传送门

废话不多说,接下来的内容中,将介绍如何将 Angular2 整合到 ASP.NET Core 中,并实现一个Anguar2 和 ASP.NET Core Web API 的身份认证。

 

注意:本文章属于Step by step + Code Sample教程,且篇幅较长,建议下载本Sample并跟着本文进度自己重做一遍本例,下载完整代码并分析代码结构才有意义,下载地址:How to authorization Angular 2 app with asp.net core web api

1.前期准备

  • 推荐使用VS2015 Update3或更新的版本完成本示例,下载地址:https://www.visualstudio.com/

  • 你需要安装.NET Core开发环境,这里提供VS版:https://www.microsoft.com/net/core#windows

  • 安装Node.js 版本5.0.0或以上,(在本例中,这个主要是编译TypeScript用的)下载地址:Node.js and NPM

  • NPM 3.0.0或以上,默认NPM会随着Node.js一并安装完毕。(在本例中,这个主要是下载各种Angular的各个包用的,参考VS中的Nuget)

2.创建项目

在VS中新建项目,项目类型选择 ASP.NET Core Web Application(.Net Core),输入项目名称为:CSAuthorAngular2InASPNetCore,Template选择为Empty.

3.在项目中整合Angular2

3.1.配置Startup.cs

注:添加下面的代码时IDE会报代码错误,这是因为还没有引用对用的包,进入报错的这一行,点击灯泡,加载对应的包就可以了。

(图文无关)

在ConfigureServices中添加如下代码

services.AddMvc();

这里是添加MVC服务

在Configure中添加如下代码

app.UseStaticFiles();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}");
});

第一句是启用静态文件,第二句是应用MVC模式并添加路由配置。

 

完整的代码应该是这个样子


3.2.添加控制器以及视图

3.2.1.在项目根目录下添加Controllers目录,并在其中添加一个控制器HomeController.cs,默认代码即可。

3.2.2.在项目跟目录下创建Views目录,在Views目录中新建目录Home, 最后在Home目录中新建视图Index.cshtml,内容应该是这样:


 现在运行项目的话你仅仅能看到一个Loading,再控制台中你还能看到错误,这是因为我们还没有配置Angular。让我们前往wwwroot目录。

3.3.在项目的wwwroot目录中添加如下结构:

3.3.1搭建Angular2基础环境

  • package.json



  • systemjs.config.js



  • tsconfig.js

  • {"compileOnSave": true,  "compilerOptions": {    "target": "es5",    "module": "commonjs",    "moduleResolution": "node",    "sourceMap": true,    "emitDecoratorMetadata": true,    "experimentalDecorators": true,    "removeComments": false,    "noImplicitAny": false  },  "exclude": [    "node_modules"  ] }
  • typings.json(注,在最新文档中typings已被npm的@types替代,参见官方文档:文档变更日志)

  • {"globalDependencies": {    "core-js": "registry:dt/core-js#0.0.0+20160725163759",    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",    "node": "registry:dt/node#6.0.0+20160909174046"  } }

右击wwwroot中的Package.json,选择Restore Packages(或者在CMD下进入wwwroot目录,并执行命令 npm install),npm会去下载需要的包,并存储于node_modules目录中。

3.3.2.配置启动文件以启用Angular2

在wwwroot下新建目录app,app拥有如下文件:

  • app.component.ts

  • import { Component } from '@angular/core';@Component({moduleId: module.id,selector: 'my-app',template: "this is in angular2",
    })
    export class AppComponent {
    }
  • 可以发现被@Component装饰属性装饰了AppComponent,selector指代你Component的占位符,比如本例中你可以再Home/index.cshtml中发现一段这样的标记

  • <my-app>Loading...</my-app>

    template既为该Component的View,不要忘记moduleId,不添加它会出现很多奇怪的问题。

  • app.module.ts

  • import { NgModule } from "@angular/core";
    import { BrowserModule } from "@angular/platform-browser";import { AppComponent } from "./app.component";@NgModule({bootstrap: [AppComponent],imports: [BrowserModule],declarations: [AppComponent]
    })
    export class AppModule { }


  • main.ts

    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    import { AppModule } from './app.module';
    const platform = platformBrowserDynamic();
    platform.bootstrapModule(AppModule);

基础整合完毕。

按F5 Debug一下,现在你能再浏览器中看到一句话:this is in angular 2

 

---分割线-------------------------------------------------------------------------

4.实现身份认证

废了半天劲,看着很傻,没有任何成就感。怎么办,让我们再深入一点,接下来我们来为Angular2完成一个Token base的身份验证,我会把Angular2的routing,data bind,service,http,等等你工作中最常用到的挨个演示一遍。

4.1.Server端

4.1.1.创建一些辅助类

4.1.1.1.在项目根目录下创建一个文件夹Auth,并添加RSAKeyHelper.cs以及TokenAuthOption.cs两个文件

在RSAKeyHelper.cs中

using System.Security.Cryptography;

namespace CSTokenBaseAuth.Auth {  
   
public class RSAKeyHelper{        
       
public static RSAParameters GenerateKey(){        
           
using (var key = new RSACryptoServiceProvider(2048)){                return key.ExportParameters(true);}}} }

在TokenAuthOption.cs中

using System;
using Microsoft.IdentityModel.Tokens;

  • namespace CSTokenBaseAuth.Auth {  
  •   public class TokenAuthOption{     
  •    public static string Audience { get; } = "ExampleAudience";       
  •  public static string Issuer { get; } = "ExampleIssuer";       
  •  public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey());        public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature);      
  •   public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(20);}
    }

4.1.1.2.在项目根目录下创建目录Model,并在其中添加RequestResult.cs,代码应该是这样。

public class RequestResult
{  
  
public RequestState State { get; set; }  
  
public string Msg { get; set; }    
  public Object Data { get; set; } }
public enum RequestState {Failed = -1,NotAuth = 0,Success = 1}

4.1.2更新Startup.cs

在ConfigureServices中添加如下代码:

services.AddAuthorization(auth =>{auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder().AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌).RequireAuthenticatedUser().Build());
});

这里是添加身份认证服务

在Configure方法中添加如下代码:

app.UseExceptionHandler(appBuilder =>{appBuilder.Use(async (context, next) =>{        var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;        //when authorization has failed, should retrun a json message to clientif (error != null && error.Error is SecurityTokenExpiredException){context.Response.StatusCode = 401;context.Response.ContentType = "application/json";await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult{State = RequestState.NotAuth,Msg = "token expired"}));}        //when orther error, retrun a error message json to clientelse if (error != null && error.Error != null){context.Response.StatusCode = 500;context.Response.ContentType = "application/json";await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult{State = RequestState.Failed,Msg = error.Error.Message}));}        //when no error, do next.else await next();});
});

本段是Handle当身份认证失败时抛出的异常,并返回合适的json

在相同的方法中添加另外一段代码:


本段代码是应用JWTBearerAuthentication身份认证。

4.1.3.TokenAuthController.cs

在Controllers中新建一个Web API Controller Class,命名为TokenAuthController.cs。我们将在这里完成登录授权,

在同文件下添加两个类,分别用来模拟用户模型,以及用户存储,代码应该是这样:

public class User
{   
  
public Guid ID { get; set; }  
  
public string Username { get; set; }
   
public string Password { get; set; } }

public static class UserStorage {  
    
public static List<User> Users { get; set; } = new List<User> {        new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },        new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },        new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }}; }

接下来在TokenAuthController.cs中添加如下方法


该方法仅仅只是生成一个Auth Token,接下来我们来添加另外一个方法来调用它

在相同文件中添加如下代码


接下来我们来完成授权部分,在相同的文件中添加如下代码:

public string GetUserInfo()
{    var claimsIdentity = User.Identity as ClaimsIdentity;    return JsonConvert.SerializeObject(new RequestResult{State = RequestState.Success,Data = new{UserName = claimsIdentity.Name}});
}

为方法添加装饰属性

[HttpGet]
[Authorize("Bearer")]

第二行代码说明这个action需要身份验证。

该文件完整的代码应该是这个样子:

using System; 
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using CSAuthorAngular2InASPNetCore.Auth;
using System.IdentityModel.Tokens.Jwt;
using Newtonsoft.Json;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.IdentityModel.Tokens;
using CSAuthorAngular2InASPNetCore.Model;
using Microsoft.AspNetCore.Authorization;

namespace CSAuthorAngular2InASPNetCore.Controllers {[Route("api/[controller]")]  
   
public class TokenAuthController : Controller{[HttpPost]      
        
public string GetAuthToken([FromBody]User user){        
               
var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);            if (existUser != null){            
         
var requestAt = DateTime.Now;                         var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;          
        
var token = GenerateToken(existUser, expiresIn);                return JsonConvert.SerializeObject(new RequestResult{State = RequestState.Success,Data = new{requertAt = requestAt,expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds,tokeyType = TokenAuthOption.TokenType,accessToken = token}});}        
           
else{            
return JsonConvert.SerializeObject(new RequestResult{State = RequestState.Failed,Msg = "Username or password is invalid"});}}    
private string GenerateToken(User user, DateTime expires){            var handler = new JwtSecurityTokenHandler();ClaimsIdentity identity = new ClaimsIdentity(            
    
new GenericIdentity(user.Username, "TokenAuth"),                new[] {                    new Claim("ID", user.ID.ToString())});            

var securityToken = handler.CreateToken(new SecurityTokenDescriptor{Issuer = TokenAuthOption.Issuer,Audience = TokenAuthOption.Audience,SigningCredentials = TokenAuthOption.SigningCredentials,Subject = identity,Expires = expires});            return handler.WriteToken(securityToken);}[HttpGet][Authorize("Bearer")]        public string GetUserInfo(){            var claimsIdentity = User.Identity as ClaimsIdentity;            return JsonConvert.SerializeObject(new RequestResult{State = RequestState.Success,Data = new{UserName = claimsIdentity.Name}});}}

   
public class User{      
       
public Guid ID { get; set; }  

       
public string Username { get; set; }                            public string Password { get; set; }}  

    
public static class UserStorage{    
       
public static List<User> Users { get; set; } = new List<User> {            new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },            new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },            new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }};} }

 

4.2Angular2端

4.2.1创建View Model

在wwwroot/app下创建一个目录:_model, 并添加一个Typescript文件RequestResult.ts,内容应该是这样。

export class RequestResult {State: number;Msg: string;Data: Object;
}

 

4.2.2创建Service

在wwwroot/app下创建一个目录:_services,并添加一个Typescript文件auth.service.ts,内容应该是这样。



本文件主要用来完成登录以及登录验证工作,之后该service将可以被注入到Component中以便被Component调用。

注:主要的逻辑都应该写到service中

4.2.3.创建Component

4.2.3.1.在wwwroot/app下创建一个目录home,该目录用来存放HomeComponent,home应拥有如下文件:

  • home.component.ts


  • 查阅代码,在@Component中指定了View以及style。

    AuthService被在构造方法中被注入了本Component,ngOnInit是接口OnInit的一个方法,他在Component初始化时会被调用。

  • style.css

    /*styles of this view*/

    本例中没有添加任何样式,如有需要可以写在这里。

  • view.html

    <div *ngIf="isLogin"><h1>Hi <span>{{userName}}</span></h1></div><div *ngIf="!isLogin"><h1>please login</h1><a routerLink="/login">Login</a></div>

    *ngIf=""是Angular2 的其中一种标记语法,作用是当返回真时渲染该节点,完整教程请参阅官方文档。

4.2.3.2.在wwwroot/app下创建目录Login,该目录用来存放LoginComponent,文件结构类似于上一节。

  • login.component.ts


  • style.css

  • /*styles of this view*/
  • view.html

    <table><tr><td>userName:</td><td><input [(ngModel)]="userName" placeholder="useName:try type user1" /></td></tr><tr><td>userName:</td><td><input [(ngModel)]="password" placeholder="password:try type user1psd" /></td></tr><tr><td></td><td><input type="button" (click)="login()" value="Login" /></td></tr></table>


4.2.4.应用路由

路由是切换多页面用的。

在wwwroot/app下新建一个Typescript文件,命名为app-routing.module.ts,内容应该是这个样子。


接下来我们来应用这个路由,

打开app.module.ts,更新代码如下:

NgModule和BrowserModule你可以理解为基础模块,必加的。

HttpModule是做http请求用的。

FormsModule是做双向数据绑定用的,比如下面这样的,如果想把数据从view更新到component,就必须加这个。

<input [(ngModel)]="userName" placeholder="useName:try type user1" />

 AppRoutingModule即为我们刚才添加的路由文件。

AuthService是我们最早添加的service文件。

AppComponent是我们最初添加的那个app.component.ts里的那个component.

HomeComponent,LoginComponent同上。 

最后我们再app.component.ts中添加路由锚点,

把template的值为 "<router-outlet></router-outlet>"

完整的代码应该是这样:

import { Component } from '@angular/core';@Component({moduleId: module.id,selector: 'my-app',template: "<router-outlet></router-outlet>",
})
export class AppComponent {
}

router-outlet是路由锚点的关键词。 

至此,所有代码完成,F5调试吧。

 完整的Angular2的入门教程,请参阅官方文档的《英雄指南》:中文传送门 | 英文传送门 

关于本例完整的代码以及调试运行步骤,请访问:How to authorization Angular 2 app with asp.net core web api

相关文章

  • Angular 2与TypeScript概览

  • Angular发布1.5正式版,专注于向Angular 2的过渡

  • 我为什么选择Angular 2?

  • ASP.NET Core loves JavaScript

  • 使用 JavaScriptService 在.NET Core 里实现DES加密算法

原文地址:http://www.cnblogs.com/onecodeonescript/p/6062203.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/327820.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

xml vs db.properties

xml vs db.properties <property name"url" value"jdbc:mysql://localhost:3306/mybatis?useSSLtrue&amp;useUnicodetrue&amp;characterEncodingUTF-8"/>urljdbc:mysql://localhost:3306/mybatis? useSSLtrue&useUnicodetrue&cha…

require.ensure与require AMD的区别

转载自 webpack: require.ensure与require AMD的区别 webpack: require.ensure与require AMD的区别 简介 require-ensure和require-amd的区别&#xff1a; require-amd 说明: 同AMD规范的require函数&#xff0c;使用时传递一个模块数组和回调函数&#xff0c;模块都被下载下来…

python django 是啥_python的django做什么的

Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的&#xff0c;即是CMS(内容管理系统)软件。并于2005年7月在BSD许可…

org.springframework.amqp.AmqpConnectException java.net.ConnectException的解决办法

一、报错信息 spring cloud集成rabbitmq时报错&#xff1a; org.springframework.amqp.AmqpTimeoutException: java.util.concurrent.TimeoutException at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptio…

hibernate注解实体类(Dept.java)

Dept.java 部门信息表的实体类详情 package cn.bdqn.hibernate_Criteria.entity;import java.util.HashSet; import java.util.Set;import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; i…

.NET Core 1.1 升级公告

2016年11月16日发布.NET Core 1.1 。 它包括对其他Linux发行版的支持&#xff0c;有很多更新&#xff0c;是当前的第一个版本。 将在下面描述所有这些变化。 它是“Go Live”&#xff0c;可用于生产工作负载。 您可以立即下载版本&#xff1a;: Windows x64Windows x86macOS …

Java IO: InputStreamReader和OutputStreamWriter

转载自 Java IO: InputStreamReader和OutputStreamWriter作者: Jakob Jenkov 译者: 李璟(jlee381344197gmail.com) 本章节将简要介绍InputStreamReader和OutputStreamWriter。细心的读者可能会发现&#xff0c;在之前的文章中&#xff0c;IO中的类要么以Stream结尾&#xff0c…

java常用代码_Java 中常用代码 (欢迎补充)

//------------------------------------------------------------------------------------------------------生成四位随即验证码String str "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";StringBuilder s new StringBuilder();int len 4…

Intellij IDEA 重置所有工具栏 Intellij IDEA 中的 Debug 控制台输出窗口不见了的解决办法

Intellij IDEA 中的 Debug 控制台输出窗口不见了的解决办法 在 Debug 工具窗口&#xff0c;如图点击左侧重置布局图标&#xff0c;你的console窗口就还原了。

基于 .Net Core 的组件 Nuget 包制作 amp; 发布

微软的 ASP.Net Core 强化了 Nuget 的使用&#xff0c;所有的 .Net Core 组件均有 Nuget 管理&#xff0c;所以有必要探讨一下 .Net Core 组件制作 Nuget 包和发布。 之前 .Net Framework 程序集打包 Nuget 有以下方法&#xff1a; 1. 使用命令 nuget pack 详见博客园的一篇博…

#{} vs ${}

#{}: 是以预编译的形式&#xff0c;将参数设置到SQL语句中&#xff1b;PreparedStatement:防止SQL注入 ${}: 取出的值直接拼装在SQL语句中&#xff1b;会有安全问题 大多数情况下&#xff0c;我们去参数的值都应该使用#{}

Java IO: Reader和Writer

转载自 Java IO: Reader和Writer作者: Jakob Jenkov 译者: 李璟(jlee381344197gmail.com) Reader 原文链接 Reader是Java IO中所有Reader的基类。Reader与InputStream类似&#xff0c;不同点在于&#xff0c;Reader基于字符而非基于字节。换句话说&#xff0c;Reader用于读取…

idea新建module 后 mapper老是说mapper和xml没有绑定

20200225_005a 三层全部走通 上面走不出来应该是spingboot的版本太高了 选spring版本的时候选到最低版本 intian_talentapartment_api.zip repair

java线程的创建线程_多线程(Thread、线程创建、线程池)

第1章 多线程1.1 多线程介绍学习多线程之前&#xff0c;我们先要了解几个关于多线程有关的概念。进程&#xff1a;进程指正在运行的程序。确切的来说&#xff0c;当一个程序进入内存运行&#xff0c;即变成一个进程&#xff0c;进程是处于运行过程中的程序&#xff0c;并且具有…

hibernate注解实体类(Emp.java)

Emp.java 员工信息表的注解实体类详情&#xff1a; package cn.bdqn.hibernate_Criteria.entity;import java.util.Date; import javax.persistence.*;/*** Emp entity. author MyEclipse Persistence Tools*/Entity Table(name"EMP") public class Emp implements j…

MySQL在EF Core下的Scaffolding操作

Pomelo于2016年11月22日发布的MySQL for ef core中&#xff0c;增加了DbContext Scaffolding的支持&#xff0c;这项功能是让开发者可以从现有的数据库中提取表、列、索引及外键成为模型。这也是当前唯一一个支持Scaffolding的MySQL for EF Core Provider。 下面将演示如何使用…

Java IO: 序列化与ObjectInputStream、ObjectOutputStream

转载自 Java IO: 序列化与ObjectInputStream、ObjectOutputStream作者&#xff1a;Jakob Jenkov 译者&#xff1a; 李璟(jlee381344197gmail.com) 本小节会简要概括Java IO中的序列化以及涉及到的流&#xff0c;主要包括ObjectInputStream和ObjectOutputStream。 Serializabl…