├── ASPNETCoreAngularJWT.csproj ├── Common ├── RSAKeyHelper.cs ├── RequestResult.cs ├── TokenAuthOption.cs ├── User.cs └── UserStorage.cs ├── Controllers ├── HomeController.cs ├── TokenAuthController.cs └── ValuesController.cs ├── LICENSE ├── Program.cs ├── README.md ├── Startup.cs ├── Views └── Home │ └── Index.cshtml ├── appsettings.Development.json ├── appsettings.json ├── package.json ├── tsconfig.json └── wwwroot ├── app ├── about │ └── about.component.ts ├── app.component.html ├── app.component.ts ├── app.module.ts ├── app.routing.ts ├── home │ ├── home.component.html │ └── home.component.ts ├── login │ └── login.component.ts ├── main.ts ├── my.material.moudule.ts ├── secret │ └── secret.component.ts └── shared │ ├── AuthBearerInterface.ts │ └── auth.service.ts ├── styles.css └── systemjs.config.js /ASPNETCoreAngularJWT.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Common/RSAKeyHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | 4 | namespace ASPNETCoreAngularJWT 5 | { 6 | public class RSAKeyHelper 7 | { 8 | public static RSAParameters GenerateKey() 9 | { 10 | using (var key = new RSACryptoServiceProvider(2048)) 11 | { 12 | return key.ExportParameters(true); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Common/RequestResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace ASPNETCoreAngularJWT 5 | { 6 | public class RequestResult 7 | { 8 | public RequestState State { get; set; } 9 | public string Msg { get; set; } 10 | public Object Data { get; set; } 11 | } 12 | 13 | public enum RequestState 14 | { 15 | Failed = -1, 16 | NotAuth = 0, 17 | Success = 1 18 | } 19 | } -------------------------------------------------------------------------------- /Common/TokenAuthOption.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.IdentityModel.Tokens; 2 | using System; 3 | 4 | 5 | 6 | namespace ASPNETCoreAngularJWT 7 | { 8 | public class TokenAuthOption 9 | { 10 | public static string Audience { get; } = "MyAudience"; 11 | public static string Issuer { get; } = "MyIssuer"; 12 | public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey()); 13 | public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature); 14 | 15 | public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(40); 16 | public static string TokenType { get; } = "Bearer"; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Common/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | 5 | namespace ASPNETCoreAngularJWT 6 | { 7 | public class User 8 | { 9 | public Guid ID { get; set; } 10 | public string Username { get; set; } 11 | 12 | public string Password { get; set; } 13 | 14 | public string Fname {get;set;} 15 | public string Lname {get;set;} 16 | 17 | } 18 | 19 | 20 | } -------------------------------------------------------------------------------- /Common/UserStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ASPNETCoreAngularJWT 5 | { 6 | public static class UserStorage 7 | { 8 | public static List Users { get; set; } = new List { 9 | new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd", Fname="user1", Lname="Lname1" }, 10 | new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd", Fname="user2", Lname="Lname2" }, 11 | new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd", Fname="user3", Lname="Lname3" } 12 | }; 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace ASPNETCoreAngularJWT 6 | { 7 | public class HomeController : Controller 8 | { 9 | public HomeController(ILogger logger) 10 | { 11 | _logger = logger; 12 | } 13 | 14 | 15 | public IActionResult Index() 16 | { 17 | _logger.LogError("HomeController: Helloworld from Index"); 18 | return View(); 19 | } 20 | private readonly ILogger _logger; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Controllers/TokenAuthController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | //using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.IdentityModel.Tokens.Jwt; 6 | using System.Security.Claims; 7 | using System.Security.Principal; 8 | using Microsoft.IdentityModel.Tokens; 9 | using Microsoft.AspNetCore.Authorization; 10 | using Microsoft.AspNetCore.Authentication.JwtBearer; 11 | 12 | 13 | namespace ASPNETCoreAngularJWT 14 | { 15 | 16 | [Route("api/[controller]")] 17 | public class TokenAuthController : Controller 18 | { 19 | 20 | 21 | [HttpPut("Login")] 22 | public IActionResult Login([FromBody]User user) 23 | { 24 | User existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password); 25 | 26 | if (existUser != null) 27 | { 28 | 29 | var requestAt = DateTime.Now; 30 | var expiresIn = requestAt + TokenAuthOption.ExpiresSpan; 31 | var token = GenerateToken(existUser, expiresIn); 32 | 33 | return Json(new RequestResult 34 | { 35 | State = RequestState.Success, 36 | Data = new 37 | { 38 | requertAt = requestAt, 39 | expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds, 40 | tokeyType = TokenAuthOption.TokenType, 41 | accessToken = token 42 | } 43 | }); 44 | } 45 | else 46 | { 47 | return Json(new RequestResult 48 | { 49 | State = RequestState.Failed, 50 | Msg = "Username or password is invalid" 51 | }); 52 | } 53 | } 54 | 55 | private string GenerateToken(ASPNETCoreAngularJWT.User user, DateTime expires) 56 | { 57 | var handler = new JwtSecurityTokenHandler(); 58 | 59 | ClaimsIdentity identity = new ClaimsIdentity( 60 | new GenericIdentity(user.Username, "TokenAuth"), 61 | new[] { new Claim("ID", user.ID.ToString())} 62 | ); 63 | 64 | var securityToken = handler.CreateToken(new SecurityTokenDescriptor 65 | { 66 | Issuer = TokenAuthOption.Issuer, 67 | Audience = TokenAuthOption.Audience, 68 | SigningCredentials = TokenAuthOption.SigningCredentials, 69 | Subject = identity, 70 | Expires = expires 71 | }); 72 | return handler.WriteToken(securityToken); 73 | } 74 | 75 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 76 | [HttpGet] 77 | public IActionResult GetUserInfo() 78 | { 79 | var claimsIdentity = User.Identity as ClaimsIdentity; 80 | return Json(new RequestResult 81 | { 82 | State = RequestState.Success, 83 | Data = new { UserName = claimsIdentity.Name } 84 | }); 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Authentication.JwtBearer; 6 | 7 | namespace ASPNETCoreAngularJWT 8 | { 9 | [Route("api/[controller]")] 10 | public class ValuesController : Controller 11 | { 12 | // GET api/values 13 | [AllowAnonymous] 14 | [HttpGet] 15 | public IEnumerable Get() 16 | { 17 | return new string[] { "value1", "value2" }; 18 | } 19 | 20 | // GET api/values/5 21 | [HttpGet("{id}")] 22 | public string Get(int id) 23 | { 24 | return "value"; 25 | } 26 | 27 | // POST api/values 28 | [HttpPost] 29 | public void Post([FromBody]string value) 30 | { 31 | } 32 | 33 | // PUT api/values/5 34 | [HttpPut("{id}")] 35 | public void Put(int id, [FromBody]string value) 36 | { 37 | } 38 | 39 | // DELETE api/values/5 40 | [HttpDelete("{id}")] 41 | public void Delete(int id) 42 | { 43 | } 44 | 45 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 46 | [HttpGet("GetStaff")] 47 | public IActionResult GetStaff() 48 | { 49 | List model = new List(); 50 | foreach (User user in UserStorage.Users ){ 51 | model.Add(user.Username); 52 | } 53 | return Json(model); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace ASPNETCoreAngularJWT 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | CreateHostBuilder(args).Build().Run(); 19 | } 20 | 21 | public static IHostBuilder CreateHostBuilder(string[] args) => 22 | Host.CreateDefaultBuilder(args) 23 | .ConfigureWebHostDefaults(webBuilder => 24 | { 25 | webBuilder.UseStartup(); 26 | }); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASPNETcoreAngularJWT 2 | 3 | Here is very basic show case for jwt bearer token authorization, using angular as front end and ASP.NET Core as backend. 4 | ASP.NET Core3.1 angular7.2.1 with systemjs. 5 | (Make sure you have dotnet core 3.1 + and Angular7.2.1 installed) 6 | 7 | *********************************************************** 8 | 9 | (Why systemjs not webpack? Personal opinion: webpack just too complicated to use for any small size, simple application, I rather scarified efficient to let most programmers can read my example code) 10 | 11 | ************************************************************************** 12 | 13 | 14 | Now you can stop google and find out how to do bear JWT token Authentication, it is simple! 15 | 16 | At backend: MS$ provide middleware AddAuthentication().AddJwtBearer() for bear JWT token Authentication, so, things turns to be as simple as add the middleware at Startup.cs 17 | 18 | At frontend(angular in this case): save the token in sessionStorage, and when talking to backend, set header of http(https for production) request as: 19 | 20 | let headers = new HttpHeaders() 21 | .set('Content-Type','application/json') 22 | .set("Authorization", "Bearer " + token); 23 | 24 | ************************************************************ 25 | Angular & material 7.2.1 in ASP.NET Core 3.1 (VS2019 or csproj version)with JWT solution. 26 | 27 | Make sure: 28 | 29 | ASP.NET CORE 3.1 has been installed, if not, get from here: 30 | https://www.microsoft.com/net/download/core 31 | 32 | nodejs has been installed, if not, get from https://nodejs.org/en/download/ 33 | 34 | after download this repositiry, go to folder with file ASPNETCoreAngularJWT.csproj and run command: 35 | 36 | >dotnet restore 37 | 38 | >npm install 39 | 40 | >npm start 41 | 42 | Finally start dotnet and browser http://localhost:5000 by 43 | 44 | >dotnet run 45 | 46 | And do not forget to run "npm start" after modify any typescript files. 47 | 48 | You can run this for angular compile and dotnet run 49 | 50 | >npm run runall 51 | 52 | Also it can be opened by both Visual Studio 2017 or Visual Studio Code. 53 | After open by VS2017 and before run as debug, make sure project-> property -> Debug and App URL ="http://localhost:5000" and rebuild again before start to debug 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.FileProviders; 9 | using Microsoft.Extensions.Logging; 10 | using Serilog; 11 | using Microsoft.AspNetCore.Diagnostics; 12 | using Microsoft.IdentityModel.Tokens; 13 | using Newtonsoft.Json; 14 | 15 | 16 | namespace ASPNETCoreAngularJWT 17 | { 18 | public class Startup 19 | { 20 | public Startup(IConfiguration configuration) 21 | { 22 | Configuration = configuration; 23 | 24 | } 25 | 26 | public IConfiguration Configuration { get; } 27 | 28 | // This method gets called by the runtime. Use this method to add services to the container. 29 | public void ConfigureServices(IServiceCollection services) 30 | { 31 | services.AddControllersWithViews(); 32 | // Enable the use of an [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] attribute on methods and classes to protect. 33 | services.AddAuthentication().AddJwtBearer(cfg => 34 | { 35 | cfg.RequireHttpsMetadata = false; 36 | cfg.SaveToken = true; 37 | 38 | cfg.TokenValidationParameters = new TokenValidationParameters() 39 | { 40 | IssuerSigningKey = TokenAuthOption.Key, 41 | ValidAudience = TokenAuthOption.Audience, 42 | ValidIssuer = TokenAuthOption.Issuer, 43 | // When receiving a token, check that we've signed it. 44 | ValidateIssuerSigningKey = true, 45 | // When receiving a token, check that it is still valid. 46 | ValidateLifetime = true, 47 | // This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time 48 | // when validating the lifetime. As we're creating the tokens locally and validating them on the same 49 | // machines which should have synchronised time, this can be set to zero. and default value will be 5minutes 50 | ClockSkew = TimeSpan.FromMinutes(0) 51 | }; 52 | 53 | }); 54 | 55 | 56 | // Add framework services. 57 | services.AddMvc(); 58 | } 59 | 60 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 61 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 62 | { 63 | #region logger 64 | // loggerFactory.AddSerilog(); 65 | // loggerFactory.AddConsole(Configuration.GetSection("Logging")); 66 | // loggerFactory.AddDebug(); 67 | #endregion 68 | 69 | #region static files 70 | app.UseStaticFiles(); 71 | 72 | app.UseStaticFiles(new StaticFileOptions 73 | { 74 | FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "node_modules")), 75 | RequestPath = "/node_modules" 76 | }); 77 | #endregion 78 | 79 | #region Handle Exception 80 | app.UseExceptionHandler(appBuilder => 81 | { 82 | appBuilder.Use(async (context, next) => 83 | { 84 | var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature; 85 | 86 | //when authorization has failed, should retrun a json message to client 87 | if (error != null && error.Error is SecurityTokenExpiredException) 88 | { 89 | context.Response.StatusCode = 401; 90 | context.Response.ContentType = "application/json"; 91 | 92 | await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult 93 | { 94 | State = RequestState.NotAuth, 95 | Msg = "token expired" 96 | })); 97 | } 98 | //when orther error, retrun a error message json to client 99 | else if (error != null && error.Error != null) 100 | { 101 | context.Response.StatusCode = 500; 102 | context.Response.ContentType = "application/json"; 103 | await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult 104 | { 105 | State = RequestState.Failed, 106 | Msg = error.Error.Message 107 | })); 108 | } 109 | //when no error, do next. 110 | else await next(); 111 | }); 112 | }); 113 | #endregion 114 | app.UseAuthentication(); 115 | app.UseRouting(); 116 | app.UseAuthorization(); 117 | #region route 118 | 119 | app.UseEndpoints(endpoints => 120 | { 121 | endpoints.MapControllerRoute( 122 | name: "default", 123 | pattern: "{controller=Home}/{action=Index}/{id?}"); 124 | }); 125 | #endregion 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ASP.NET Angular JWT 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | Loading... 21 | 22 | 23 | -------------------------------------------------------------------------------- /appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.3.0", 3 | "name": "asp.net.core.ng.jwt", 4 | "private": true, 5 | "scripts": { 6 | "start": "tsc", 7 | "tsc": "tsc", 8 | "tsc:w": "tsc -w", 9 | "runall": "tsc && dotnet run" 10 | }, 11 | "keywords": [ 12 | "angular", 13 | "httpclient", 14 | "lattable", 15 | "operators", 16 | "material", 17 | "systemjs", 18 | "system.config.js", 19 | "dotnetcore", 20 | "jwt", 21 | "bearer", 22 | "token", 23 | "authentication", 24 | "api" 25 | ], 26 | "dependencies": { 27 | "@angular/animations": "7.2.1", 28 | "@angular/cdk": "7.2.1", 29 | "@angular/common": "7.2.1", 30 | "@angular/http": "7.2.1", 31 | "@angular/compiler": "7.2.1", 32 | "@angular/core": "7.2.1", 33 | "@angular/forms": "7.2.1", 34 | "@angular/material": "7.2.1", 35 | "@angular/platform-browser": "7.2.1", 36 | "@angular/platform-browser-dynamic": "7.2.1", 37 | "@angular/platform-server": "7.2.1", 38 | "@angular/router": "7.2.1", 39 | "core-js": "2.6.1", 40 | "hammerjs": "2.0.8", 41 | "rxjs": "6.3.3", 42 | "rxjs-compat": "6.2.1", 43 | "rxjs-system-bundle": "6.0.0", 44 | "systemjs": "0.21.4", 45 | "typescript": "3.1.1", 46 | "zone.js": "0.8.29" 47 | }, 48 | "devDependencies": { 49 | "@types/node": "10.9.4" 50 | } 51 | } 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "removeComments": false, 11 | "noImplicitAny": false, 12 | "lib": [ "es2015", "dom" ] 13 | }, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } -------------------------------------------------------------------------------- /wwwroot/app/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | moduleId: module.id, 5 | template: ` 6 |
7 |
8 |

About

9 |
10 |
11 | 12 |
13 |
14 | ` 15 | }) 16 | export class AboutComponent { } 17 | -------------------------------------------------------------------------------- /wwwroot/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Home 5 | About 6 | secret 7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 |

ASP.NET Core 3.1 And Angular(7.2.1) implement of: https://code.msdn.microsoft.com/How-to-achieve-a-bearer-9448db57

15 |

This is for learning purpose only

16 |
17 | -------------------------------------------------------------------------------- /wwwroot/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { AuthService } from "./shared/auth.service"; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | selector: 'app-root', 8 | templateUrl: 'app.component.html' 9 | }) 10 | export class AppComponent { 11 | constructor(private authService: AuthService, private router: Router) {} 12 | 13 | public logout(){ 14 | sessionStorage.clear(); 15 | this.router.navigate(["login"]); 16 | } 17 | } -------------------------------------------------------------------------------- /wwwroot/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | 7 | import { MyMaterialModule} from './my.material.moudule' 8 | import { AppComponent } from './app.component'; 9 | import { HomeComponent } from './home/home.component'; 10 | import { AboutComponent } from './about/about.component'; 11 | import { routing } from './app.routing'; 12 | import { LoginComponent } from './login/login.component'; 13 | import { SecretComponent } from './secret/secret.component'; 14 | import { AuthService } from "./shared/auth.service"; 15 | 16 | //import 'hammerjs/hammer'; 17 | 18 | @NgModule({ 19 | imports: [ 20 | BrowserModule, 21 | BrowserAnimationsModule, 22 | FormsModule, 23 | HttpClientModule, 24 | MyMaterialModule, 25 | routing 26 | ], 27 | providers: [AuthService], 28 | declarations: [ 29 | AppComponent, 30 | HomeComponent, 31 | AboutComponent, 32 | LoginComponent, 33 | SecretComponent 34 | ], 35 | bootstrap: [AppComponent] 36 | }) 37 | export class AppModule { } 38 | -------------------------------------------------------------------------------- /wwwroot/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | 3 | import { HomeComponent } from './home/home.component'; 4 | import { AboutComponent } from './about/about.component'; 5 | import { LoginComponent } from './login/login.component'; 6 | import { SecretComponent } from './secret/secret.component'; 7 | import { AuthService} from './shared/auth.service'; 8 | 9 | const appRoutes: Routes = [ 10 | { path: "", redirectTo: "login", pathMatch: "full" }, 11 | { path: 'about', component: AboutComponent }, 12 | { path: 'login', component: LoginComponent }, 13 | { path: 'home', component: HomeComponent }, 14 | { path: 'secret', component: SecretComponent, canActivate: [ AuthService] } 15 | ]; 16 | 17 | 18 | export const routing = RouterModule.forRoot(appRoutes); 19 | -------------------------------------------------------------------------------- /wwwroot/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Home

4 |
5 |
6 |
7 | 8 | person 9 | {{character}} 10 | 11 | star 12 |
13 |
14 |
-------------------------------------------------------------------------------- /wwwroot/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from "../shared/auth.service"; 3 | import { Router } from '@angular/router'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | templateUrl: 'home.component.html' 8 | }) 9 | export class HomeComponent implements OnInit { 10 | characters: string[]; 11 | 12 | constructor(private authService: AuthService, private router: Router) { } 13 | ngOnInit() { 14 | if (this.authService.checkLogin()) { 15 | this.authService.authGet$("/api/Values/GetStaff").subscribe( 16 | characters => this.characters = characters 17 | ); 18 | } else { 19 | this.router.navigate(["login"]); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wwwroot/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Subscription } from 'rxjs/Subscription'; 4 | import { AuthService } from "../shared/auth.service"; 5 | 6 | @Component({ 7 | moduleId: module.id, 8 | selector: "my-login", 9 | template: ` 10 |
11 |
12 |

login

13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
user name:
password:
30 |
31 | ` 32 | }) 33 | 34 | export class LoginComponent implements OnDestroy { 35 | 36 | private userName: string; 37 | private password: string; 38 | private postStream$: Subscription; 39 | 40 | constructor(private authService: AuthService,private router: Router) { } 41 | 42 | login() { 43 | if (this.postStream$) { this.postStream$.unsubscribe } 44 | 45 | this.postStream$ = this.authService.login$(this.userName, this.password).subscribe( 46 | result => { 47 | if (result.state == 1) { 48 | this.router.navigate(["home"]); 49 | } else { 50 | alert(result.msg); 51 | } 52 | } 53 | ) 54 | } 55 | 56 | ngOnDestroy() { 57 | if(this.postStream$){this.postStream$.unsubscribe()} 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /wwwroot/app/main.ts: -------------------------------------------------------------------------------- 1 | //import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | import { AppModule } from './app.module'; 4 | import 'hammerjs/hammer'; 5 | 6 | 7 | platformBrowserDynamic().bootstrapModule(AppModule); 8 | -------------------------------------------------------------------------------- /wwwroot/app/my.material.moudule.ts: -------------------------------------------------------------------------------- 1 | 2 | import {NgModule} from '@angular/core'; 3 | import { 4 | MatAutocompleteModule, 5 | MatButtonModule, 6 | MatButtonToggleModule, 7 | MatCardModule, 8 | MatCheckboxModule, 9 | MatChipsModule, 10 | MatDatepickerModule, 11 | MatDialogModule, 12 | MatExpansionModule, 13 | MatGridListModule, 14 | MatIconModule, 15 | MatInputModule, 16 | MatListModule, 17 | MatMenuModule, 18 | MatNativeDateModule, 19 | MatPaginatorModule, 20 | MatProgressBarModule, 21 | MatProgressSpinnerModule, 22 | MatRadioModule, 23 | MatRippleModule, 24 | MatSelectModule, 25 | MatSidenavModule, 26 | MatSliderModule, 27 | MatSlideToggleModule, 28 | MatSnackBarModule, 29 | MatSortModule, 30 | MatTableModule, 31 | MatTabsModule, 32 | MatToolbarModule, 33 | MatTooltipModule, 34 | MatStepperModule 35 | } from '@angular/material'; 36 | //import {CdkTableModule} from '@angular/cdk/table'; 37 | 38 | @NgModule({ 39 | exports: [ 40 | // CdkTableModule, 41 | MatAutocompleteModule, 42 | MatButtonModule, 43 | MatButtonToggleModule, 44 | MatCardModule, 45 | MatCheckboxModule, 46 | MatChipsModule, 47 | MatStepperModule, 48 | MatDatepickerModule, 49 | MatDialogModule, 50 | MatExpansionModule, 51 | MatGridListModule, 52 | MatIconModule, 53 | MatInputModule, 54 | MatListModule, 55 | MatMenuModule, 56 | MatNativeDateModule, 57 | MatPaginatorModule, 58 | MatProgressBarModule, 59 | MatProgressSpinnerModule, 60 | MatRadioModule, 61 | MatRippleModule, 62 | MatSelectModule, 63 | MatSidenavModule, 64 | MatSliderModule, 65 | MatSlideToggleModule, 66 | MatSnackBarModule, 67 | MatSortModule, 68 | MatTableModule, 69 | MatTabsModule, 70 | MatToolbarModule, 71 | MatTooltipModule, 72 | ] 73 | }) 74 | export class MyMaterialModule {} 75 | -------------------------------------------------------------------------------- /wwwroot/app/secret/secret.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from "../shared/auth.service"; 3 | 4 | @Component({ 5 | template: ` 6 |
7 |
8 |

Secret

9 |
10 |
11 | You can get source code here: https://github.com/Longfld/ASPNETcoreAngularJWT 12 |
13 |
14 |

Hi {{userName}}

15 |
16 |
17 | ` 18 | }) 19 | export class SecretComponent implements OnInit { 20 | 21 | userName: string; 22 | 23 | constructor(private authService: AuthService) { } 24 | 25 | ngOnInit() { 26 | this.authService.getUserInfo$().subscribe( 27 | res => { 28 | if (res != null && res.data) { 29 | let thisuser = res.data 30 | if (thisuser && thisuser.userName) { 31 | this.userName = thisuser.userName; 32 | } 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wwwroot/app/shared/AuthBearerInterface.ts: -------------------------------------------------------------------------------- 1 | export interface AuthBearer{ 2 | state : number; 3 | msg : string; 4 | data : any; 5 | } -------------------------------------------------------------------------------- /wwwroot/app/shared/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | 3 | import { HttpClient, HttpHeaders, HttpInterceptor, HttpRequest, HttpResponse, HttpHandler, HttpErrorResponse, HttpEvent } from "@angular/common/http"; 4 | import { Router, CanActivate } from '@angular/router'; 5 | 6 | import { Observable, of } from 'rxjs'; 7 | import { tap, map, distinctUntilChanged, debounceTime, catchError } from 'rxjs/operators' 8 | 9 | import { AuthBearer } from './AuthBearerInterface'; 10 | 11 | @Injectable() 12 | export class AuthService implements CanActivate, HttpInterceptor { 13 | private tokeyKey = "token"; 14 | 15 | constructor(private http: HttpClient, private router: Router) { } 16 | 17 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 18 | return next.handle(request).pipe( 19 | tap( 20 | event => { 21 | // if (event instanceof HttpResponse) {} 22 | }, (error: any) => { 23 | if (error instanceof HttpErrorResponse) { 24 | if (error.status == 401) { 25 | sessionStorage.clear(); 26 | this.router.navigate(['logon']); 27 | } 28 | } 29 | } 30 | ) 31 | ) 32 | } 33 | 34 | public canActivate() { 35 | if (this.checkLogin()) { 36 | return true; 37 | } else { 38 | this.router.navigate(['login']); 39 | return false; 40 | } 41 | } 42 | 43 | public login$(userName: string, password: string) { 44 | let header = new HttpHeaders().set('Content-Type', 'application/json'); 45 | let body = JSON.stringify({ "Username": userName, "Password": password }); 46 | let options = { headers: header }; 47 | 48 | return this.http.put("/api/TokenAuth/Login", body, options).pipe( 49 | debounceTime(200), 50 | distinctUntilChanged(), 51 | map( 52 | res => { 53 | let result = res; 54 | if (result.state && result.state == 1 && result.data && result.data.accessToken) { 55 | sessionStorage.setItem(this.tokeyKey, result.data.accessToken); 56 | } 57 | return result; 58 | } 59 | ), 60 | 61 | catchError(this.handleError("login")) 62 | ) 63 | } 64 | 65 | public authGet$(url) { 66 | let header = this.initAuthHeaders(); 67 | let options = { headers: header }; 68 | return this.http.get(url, options).pipe( 69 | debounceTime(200), 70 | distinctUntilChanged(), 71 | catchError(this.handleError("authGet"))); 72 | } 73 | 74 | public checkLogin(): boolean { 75 | let token = sessionStorage.getItem(this.tokeyKey); 76 | return token != null; 77 | } 78 | 79 | public getUserInfo$() { 80 | return this.authGet$("/api/TokenAuth"); 81 | } 82 | 83 | public authPost$(url: string, body: any) { 84 | let headers = this.initAuthHeaders(); 85 | 86 | return this.http.post(url, body, { headers: headers }).pipe( 87 | debounceTime(200), 88 | distinctUntilChanged(), 89 | catchError(this.handleError("authPost")) 90 | ) 91 | } 92 | 93 | private getLocalToken(): string { 94 | return sessionStorage.getItem(this.tokeyKey); 95 | } 96 | 97 | private initAuthHeaders(): HttpHeaders { 98 | let token = this.getLocalToken(); 99 | if (token == null) throw "No token"; 100 | 101 | let headers = new HttpHeaders() 102 | .set('Content-Type', 'application/json') 103 | .set("Authorization", "Bearer " + token); 104 | return headers; 105 | } 106 | 107 | private handleError(operation = 'operation', result?: T) { 108 | return (error: any): Observable => { 109 | console.error(`${operation} error: ${error.message}`); 110 | return of(result as T); 111 | }; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /wwwroot/styles.css: -------------------------------------------------------------------------------- 1 | .mdl-tabs__tab-bar a { 2 | color: rgba(255, 255, 255, 0.8); 3 | } 4 | 5 | .mdl-tabs__tab-bar a.is-active { 6 | border-bottom: 2px solid #ff4081; 7 | color: #fff; 8 | } 9 | 10 | .mdl-tabs__tab-bar { 11 | display: inline; 12 | } 13 | 14 | header { 15 | background-color: #3f51b5; 16 | overflow: auto; 17 | } 18 | 19 | main { 20 | margin: 2em; 21 | } 22 | 23 | .card-wide { 24 | width: 100%; 25 | max-width: 500px; 26 | } 27 | 28 | .card-wide .mdl-card__title { 29 | background-color: #3f51b5; 30 | color: #fff; 31 | } 32 | 33 | .mdl-card { 34 | min-height: 0; 35 | } 36 | 37 | .mdl-card__supporting-text { 38 | color: rgba(0, 0, 0, 0.87); 39 | } -------------------------------------------------------------------------------- /wwwroot/systemjs.config.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | System.config({ 3 | paths: { 4 | // paths serve as alias 5 | 'npm:': 'node_modules/' 6 | }, 7 | // map tells the System loader where to look for things 8 | map: { 9 | // our app is within the app folder 10 | app: 'app', 11 | // angular bundles 12 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 13 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 14 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 15 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 16 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 17 | '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js', 18 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 19 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 20 | '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js', 21 | '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js', 22 | '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js', 23 | 24 | //material 25 | '@angular/material': 'npm:@angular/material/bundles/material.umd.js', 26 | '@angular/cdk/a11y': 'npm:@angular/cdk/bundles/cdk-a11y.umd.js', 27 | '@angular/cdk/bidi': 'npm:@angular/cdk/bundles/cdk-bidi.umd.js', 28 | '@angular/cdk/coercion': 'npm:@angular/cdk/bundles/cdk-coercion.umd.js', 29 | '@angular/cdk/collections': 'npm:@angular/cdk/bundles/cdk-collections.umd.js', 30 | '@angular/cdk/keycodes': 'npm:@angular/cdk/bundles/cdk-keycodes.umd.js', 31 | '@angular/cdk/observers': 'npm:@angular/cdk/bundles/cdk-observers.umd.js', 32 | '@angular/cdk/overlay': 'npm:@angular/cdk/bundles/cdk-overlay.umd.js', 33 | '@angular/cdk/platform': 'npm:@angular/cdk/bundles/cdk-platform.umd.js', 34 | '@angular/cdk/portal': 'npm:@angular/cdk/bundles/cdk-portal.umd.js', 35 | '@angular/cdk/accordion' : 'npm:@angular/cdk/bundles/cdk-accordion.umd.js', 36 | '@angular/cdk/layout' : 'npm:@angular/cdk/bundles/cdk-layout.umd.js', 37 | '@angular/cdk/scrolling': 'npm:@angular/cdk/bundles/cdk-scrolling.umd.js', 38 | '@angular/cdk/table': 'npm:@angular/cdk/bundles/cdk-table.umd.js', 39 | '@angular/cdk/text-field': 'npm:@angular/cdk/bundles/cdk-text-field.umd.js', 40 | '@angular/cdk/tree': 'npm:@angular/cdk/bundles/cdk-tree.umd.js', 41 | '@angular/cdk/stepper': 'npm:@angular/cdk/bundles/cdk-stepper.umd.js', 42 | 43 | // other libraries 44 | 'rxjs': 'npm:rxjs', 45 | 'rxjs-compat': 'npm:rxjs-compat', 46 | 'rxjs-system-bundle': 'npm:rxjs-system-bundle', 47 | 'hammerjs': 'npm:hammerjs', 48 | 'tslib': 'npm:tslib/tslib.js' 49 | }, 50 | // packages tells the System loader how to load when no filename and/or no extension 51 | packages: { 52 | app: {main: './main.js',defaultExtension: 'js'}, 53 | 'rxjs': {main: 'index.js', defaultExtension: 'js'}, 54 | 'rxjs/operators': {main: 'index.js', defaultExtension: 'js'}, 55 | hammerjs: { main: './hammer.js', defaultExtension: 'js'} 56 | } 57 | }); 58 | })(this); 59 | --------------------------------------------------------------------------------