Angular5 JWT身份验证(Spring Boot安全性)

欢迎使用带有Spring Security的angular5 jwt身份验证。在本教程中,我们将在一个angular5单页应用程序中使用jwt身份验证创建一个完整的堆栈应用程序,该应用程序具有由spring boot支持并集成了spring security的后备服务器。带有集成了HttpInterceptor的示例angular5示例应用程序,以拦截所有HTTP请求以在标头中添加jwt授权令牌,并且在服务器中,我们将使用Spring安全性公开和保护一些REST端点。仅当有效的jwt令牌有效时,该资源才能访问在标题中找到。我们将使用Mysql DB进行持久性存储。

本文由4个部分组成。在第一部分中,我们将使用材料设计来构建单页angular5应用程序。 在第二部分中,我们将创建一个带有示例REST端点公开的Spring Boot应用程序。 在第三部分中,我们将通过Spring Security与JWT集成,在第四部分中,将使用HttpIntrceptor与angular5进行jwt集成。

使用的技术

我们在角度和Spring启动中都进行了频繁的版本升级。 因此,让我们首先确认将用于构建此应用程序的这些技术的版本。

1. Spring Boot 1.5.8.RELEASE

2. jjwt 0.6.0

3.角5.2.0

4.角材料5.1.0

5. MySQL

6. Java 1.8

Jwt认证

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式来作为JSON对象在各方之间安全地传输信息。无状态身份验证机制,因为用户状态永远不会保存在服务器内存中。 JWT令牌由三部分组成,并用点号(。)分隔,即Header.payload.signature

标头使用了两种部分的令牌和哈希算法。组成这两个密钥的JSON结构是Base64Encoded。

{"alg": "HS256","typ": "JWT"
}

有效负载包含索赔。从根本上讲,索赔有三种类型:预留索赔,公共索赔和私人索赔。 保留的声明是预定义的声明,例如iss(发布者),exp(到期时间),sub(主题),aud(听众)。在私人声明中,我们可以创建一些自定义声明,例如主题,角色等。

{"sub": "Alex123","scopes": [{"authority": "ROLE_ADMIN"}],"iss": "http://devglan.com","iat": 1508607322,"exp": 1508625322
}

签名可确保令牌不会在途中发生更改。例如,如果您想使用HMAC SHA256算法,则将通过以下方式创建签名:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

服务器的受保护路由将在Authorization标头中检查有效的JWT,如果存在该JWT,则将允许用户访问受保护的资源。每当用户要访问受保护的路由或资源时,用户代理都应发送JWT,通常在使用Bearer模式的Authorization标头中。 标头的内容应如下所示:

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJBbGV4MTIzIiwic2N.v9A80eU1VDo2Mm9UqN2FyEpyT79IUmhg

创建Angular5应用程序

我们已经在上一篇文章Angular5 Material App中创建了angular5应用程序 。这是一个非常简单的集成了angular material的应用程序 。在此应用程序中,我们集成了2个模块用户和具有路由的登录模块。但是这里的登录验证是在客户端应用程序本身中进行了硬编码,一旦用户成功登录,他将被重定向到用户页面,在该页面上他可以看到数据表中的用户列表。

以下是先前的项目结构以及我们现在将要构建的项目结构。

在这个例子中,我们需要首先创建一个使用REST API的HTTP客户端,为此我们将使用@angular/common/http HttpClient。 以下是我们的app.service.ts出于演示目的,我们只有一个HTTP调用来获取用户列表。

import {Injectable} from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {User} from './user/user.model';const httpOptions = {headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};@Injectable()
export class UserService {constructor(private http: HttpClient) {}private userUrl = 'http://localhost:8080/';public getUsers(): Observable {return this.http.get(this.userUrl + '/users');}}

不要忘记在app.module.ts的提供程序中包含userService和HttpClientModule

同样,在user.component.ts我们对服务进行了以下更改并填充了数据表。还要注意的一件事是,我们在spring安全配置中禁用了/ users端点的身份验证。

import {Component, OnInit} from '@angular/core';
import {MatTableDataSource} from '@angular/material';
import {User} from './user.model';
import {UserService} from '../app.service';
import {Router} from '@angular/router';@Component({selector: 'app-root',templateUrl: './user.component.html',styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {displayedColumns = ['id', 'username', 'salary', 'age'];dataSource = new MatTableDataSource();constructor(private router: Router, private userService: UserService) {}ngOnInit(): void {this.userService.getUsers().subscribe(data => {this.dataSource.data = data;});}
}

现在,有了这样的实现,我们应该能够在URL的数据表中显示用户列表– http:// localhost:4200 / user

创建Spring Boot应用程序

首先,检查以下项目结构。 这是我们在spring boot jwt认证教程期间构建的项目。

Spring Boot应用程序的端点在控制器类的/ users中公开。 这是一个简单的实现,此外,我们为angular启用了CORS,并且用户模型类具有4个属性,如id,用户名,年龄和薪水。

UserController.java

package com.devglan.controller;import com.devglan.model.User;
import com.devglan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping(value="/users", method = RequestMethod.GET)public List listUser(){return userService.findAll();}
}

Spring安全配置

现在,我们将配置安全性以保护我们的应用程序。 现在,我们将允许/users端点进行公共访问,以便稍后可以验证jwt身份验证并在数据表中显示用户列表(如上图所示)。所有这些配置已在我的上一篇文章中讨论过-Spring Boot Security JWT身份验证。这里authenticationTokenFilterBean()无效,因为我们允许/users端点进行公共访问。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Resource(name = "userService")private UserDetailsService userDetailsService;@Autowiredprivate JwtAuthenticationEntryPoint unauthorizedHandler;@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Autowiredpublic void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(encoder());}@Beanpublic JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {return new JwtAuthenticationFilter();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable().authorizeRequests().antMatchers("/token/*").permitAll().anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);}@Beanpublic BCryptPasswordEncoder encoder(){return new BCryptPasswordEncoder();}}

在Spring Security中添加JWT身份验证

JWT的简单实现是编写一个过滤器类,该类将拦截所有请求并查找JWT授权令牌,如果在标头中找到该令牌,它将提取该令牌,解析该令牌以查找与用户相关的信息,例如username令牌被验证后,它将准备spring安全上下文,并将请求转发到过滤器链中的下一个过滤器。

因此,为此目的,我们提供了OncePerRequestFilter类,该类每个请求执行一次。 在过滤器中,我们正在对角色进行硬编码,但在实时应用程序中,我们可以从JWT令牌的自定义范围中提取它,或者在UserDetailsService进行数据库查找

JwtAuthenticationFilter.java

public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Overrideprotected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {String header = req.getHeader(HEADER_STRING);String username = null;String authToken = null;if (header != null && header.startsWith(TOKEN_PREFIX)) {authToken = header.replace(TOKEN_PREFIX,"");try {username = jwtTokenUtil.getUsernameFromToken(authToken);} catch (IllegalArgumentException e) {logger.error("an error occured during getting username from token", e);} catch (ExpiredJwtException e) {logger.warn("the token is expired and not valid anymore", e);} catch(SignatureException e){logger.error("Authentication Failed. Username or Password not valid.");}} else {logger.warn("couldn't find bearer string, will ignore the header");}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(authToken, userDetails)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));logger.info("authenticated user " + username + ", setting security context");SecurityContextHolder.getContext().setAuthentication(authentication);}}chain.doFilter(req, res);}
}

完成此实现后,我们实际上可以从WebSecurityConfig.java删除“ / users”并验证在尝试将数据加载到角度数据表中时将得到401。

在Spring Security中创建JWT令牌

我们定义了这个控制器来生成JWT令牌。该控制器will方法将在登录请求期间从客户端调用。 它将使用用户名和密码组合从数据库验证用户,并相应地生成JWT令牌。

AuthenticationController.java

@RestController
@RequestMapping("/token")
public class AuthenticationController {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Autowiredprivate UserService userService;@RequestMapping(value = "/generate-token", method = RequestMethod.POST)public ResponseEntity register(@RequestBody LoginUser loginUser) throws AuthenticationException {final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginUser.getUsername(),loginUser.getPassword()));SecurityContextHolder.getContext().setAuthentication(authentication);final User user = userService.findOne(loginUser.getUsername());final String token = jwtTokenUtil.generateToken(user);return ResponseEntity.ok(new AuthToken(token));}}

Angular5 JWT授权

现在,当要在带有Spring Security的angular5中进行JWT授权集成时,首先我们需要发出POST请求以使用用户名和密码登录。 在响应中,服务器将在成功身份验证后为您提供JWT令牌。一旦获得此令牌,我们便可以将其缓存在浏览器中以供以后的API调用重用。现在让我们定义我们的authservice,它将在以下位置请求JWT令牌登录。

验证服务

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import { HttpClient, HttpHeaders } from '@angular/common/http';@Injectable()
export class AuthService {baseUrl: 'http://localhost:8080/email2sms/';constructor(private http: HttpClient) {}attemptAuth(ussername: string, password: string): Observable {const credentials = {username: ussername, password: password};console.log('attempAuth ::');return this.http.post('http://localhost:8080/token/generate-token', credentials);}}

现在,在登录期间,我们将通过调用spring security AUTH API来调用此服务以对用户进行身份验证。

login.component.ts

import { Component, OnInit } from '@angular/core';
import {Router} from '@angular/router';
import {MatDialog} from '@angular/material';
import {AuthService} from '../core/auth.service';
import {TokenStorage} from '../core/token.storage';@Component({selector: 'app-login',templateUrl: './login.component.html',styleUrls: ['./login.component.css']
})
export class LoginComponent {constructor(private router: Router, public dialog: MatDialog, private authService: AuthService, private token: TokenStorage) {}username: string;password: string;login(): void {this.authService.attemptAuth(this.username, this.password).subscribe(data => {this.token.saveToken(data.token);this.router.navigate(['user']);});}}

手动在所有API请求的标头中添加此令牌并不是一种更干净的方法。 因此,我们将实现一个HTTPInterceptor,它将拦截所有rquest并将此JWT授权令牌添加到标头中。 此外,我们可以拦截响应,对于任何未经授权的请求或过期的令牌,我们都可以将用户重定向到登录页面。此外,要在本地存储此令牌,我们可以使用sessionstorage – sessionStorage对象仅存储一个会话的数据(该数据将被删除当浏览器标签关闭时)。 拦截器将实现HttpInterceptor接口,并重写intercept() 。 在这里,我们正在克隆设置所需标题的请求,下面是拦截器的实现。

应用拦截器

import { Injectable } from '@angular/core';
import {HttpInterceptor, HttpRequest, HttpHandler, HttpSentEvent, HttpHeaderResponse, HttpProgressEvent,HttpResponse, HttpUserEvent, HttpErrorResponse} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import {TokenStorage} from './token.storage';
import 'rxjs/add/operator/do';const TOKEN_HEADER_KEY = 'Authorization';@Injectable()
export class Interceptor implements HttpInterceptor {constructor(private token: TokenStorage, private router: Router) { }intercept(req: HttpRequest, next: HttpHandler):Observable | HttpUserEvent> {let authReq = req;if (this.token.getToken() != null) {authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + this .token.getToken())});}return next.handle(authReq).do((err: any) => {if (err instanceof HttpErrorResponse) {if (err.status === 401) {this.router.navigate(['user']);}}});}}

不要错过在app.module.ts中注册该拦截器的机会。

要将此令牌存储在浏览器存储中,让我们定义我们的token.storage.ts

import { Injectable } from '@angular/core';const TOKEN_KEY = 'AuthToken';@Injectable()
export class TokenStorage {constructor() { }signOut() {window.sessionStorage.removeItem(TOKEN_KEY);window.sessionStorage.clear();}public saveToken(token: string) {window.sessionStorage.removeItem(TOKEN_KEY);window.sessionStorage.setItem(TOKEN_KEY,  token);}public getToken(): string {return sessionStorage.getItem(TOKEN_KEY);}
}

还有客户端jwt令牌验证器,我们可以使用它来检查令牌到期。 这样做,我们不必依赖服务器来检查令牌到期。

结论

在本文中,我们了解了如何将JWT令牌与Angular5应用程序集成在一起,并具有支持的Spring Boot安全性。 如果您喜欢这篇文章,我很乐意在评论部分回覆。

翻译自: https://www.javacodegeeks.com/2018/03/angular5-jwt-authentication-spring-boot-security.html

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

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

相关文章

Java常用类(4)--System类

System类代表系统,系统级的很多属性和控制方法都放置在该类的内部,该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,可以…

Java常用类(5)--不可变的任意精度BigInteger、BigDecimal类

文章目录BigInteger类BigDecimal类BigInteger类 Integer类作为int的包装类,能存储的最大整型值为2^31-1,Long类也是有限的, 最大为2^63-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类 都无能为力。 java.math包的…

Java枚举类(1)--枚举类的定义、方法使用和接口实现

文章目录枚举类的理解枚举类的定义Enum类的主要方法枚举类实现接口枚举类的理解 当类的对象只有有限个,且确定的,称此类为枚举类。 当需要定义一组常量时,强烈建议使用枚举类。 如果枚举类中只有一个对象,则可以作为单例模式的…

java备忘录_Java 8备忘单中的可选

java备忘录Java 8 java.util.Optional<T>是scala.Option[T]和Data.Maybe在Haskell中的较差表亲。 但这并不意味着它没有用。 如果您不熟悉此概念&#xff0c;请将Optional想象为可能包含或不包含某些值的容器。 就像Java中的所有引用都可以指向某个对象或为null &#xf…

IDEA中注解注释快捷键及模板

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 文章目录单行注释多行注释文档注释(块注释)方法说明注解自动注…

让别人和自己看懂自己的程序代码?一文掌握Java单行多行、文档注释以及注解(Annotation)超详细的理解使用,IDEA注释注解快捷键和模板,提高程序代码更有可读性

文章目录单行和多行注释文档注释&#xff08;Java特有&#xff09;Annotation(注解)的理解常见的Annotation示例IDEA注释注解快捷键及模板自定义 AnnotationJDK 中的元注解单行和多行注释 注释的内容不参与编译&#xff0c;即编译以后的.class的字节码文件中不包含注释的内容。…

Java集合(1)--集合概述

Java 集合可分为 Collection 和 Map 两种体系 Collection接口&#xff1a;单列数据&#xff0c;定义了存取一组对象的方法的集合 ——List&#xff1a;元素有序、可重复的集合 ——Set&#xff1a;元素无序、不可重复的集合 Map接口&#xff1a;双列数据&#xff0c;保存具有…

win7下oracle10g安装,专门针对win7下oracle10g安装的详解

Window 7 下面安装Oracle 10g今在win7下安装oracle 10g client的时候遇到下面问题&#xff1a;在执行先决条件的时候&#xff0c;报目前只支持6.0的版本&#xff0c;修改oraparam.ini文件中的以下内容&#xff1a;[Certified Versions]#You can customise error message shown …

Java集合(3)--Iterator迭代器

Iterator对象称为迭代器(设计模式的一种)&#xff0c;主要用于遍历 Collection 集合中的元素。Collection接口继承了java.lang.Iterable接口&#xff0c;该接口有一个iterator()方法&#xff0c;那么所有实现了Collection接口的集合类都有一个iterator()方法&#xff0c;用以返…

Java集合(4)--List接口及其实现类ArrayList、LinkedList和Vector

文章目录List接口概述List接口常用方法ArrayList实现类LinkedList实现类Vector实现类List接口概述 List集合类中元素有序、且可重复&#xff0c;集合中的每个元素都有其对应的顺序索引 List容器中的元素都对应一个整数型的序号记载其在容器中的位置&#xff0c;可以根据 序号…

java hadoop_单元测试Java Hadoop作业

java hadoop在我以前的文章中&#xff0c;我展示了如何设置一个完整的基于Maven的项目&#xff0c;以用Java创建Hadoop作业。 当然并没有完成&#xff0c;因为它缺少单元测试部分。 在这篇文章中&#xff0c;我将展示如何将MapReduce单元测试添加到我之前开始的项目中。 对于单…

软件连接oracle失败怎么办,【编程开发工具】navicat连接oracle失败怎么办

Navicat连接oracle数据库时连接失败&#xff0c;出现ORA-28547错误。原因&#xff1a;navicat Primium版本的OCi和本地数据库的OCI版本不一致。解决方法&#xff1a;1、把navicat Primium版本自带oci.dll替换本地Oracle安装路径里的oci.dll。我的本地navicat Primium版本自带oc…

Java集合(5)--Set接口及其实现类HashSet、LinkedHashSet和TreeSet

文章目录Set接口概述HashSet实现类LinkedHashSet实现类TreeSet实现类Set接口概述 1、Set接口是Collection的子接口&#xff0c;set接口没有定义额外的方法&#xff0c;使用的都是Collection接口中的方法。 2、Set 集合不允许包含相同的元素&#xff0c;如果试把两个相同的元素…

Java集合(6)--Map接口

文章目录Map接口概述Map结构的理解Map接口常用方法Map接口概述 Map与Collection并列存在&#xff0c;用于保存具有映射关系的数据:key-value Map中的 key 和 value 都可以是任何引用类型的数据 常用String类作为Map的“键”。key 和 value 之间存在单向一对一关系&#xff0…

Java集合(7)--Map接口的实现类HashMap、LinkHashMap、TreeMap和Properties

文章目录HashMap类LinkedHashMap类TreeMap类Hashtable类Properties类HashMap类 1、HashMap类概述 HashMap是 Map 接口使用频率最高的实现类&#xff0c;允许使用null键和null值&#xff0c;与HashSet一样&#xff0c;不保证映射的顺序。 所有的key构成的集合是Set&#xff1a…

为什么SpringBoot如此受欢迎,以及如何有效地学习SpringBoot?

SpringBoot是最流行和使用最广泛的Java框架。 有时这种讨论“为什么SpringBoot如此受欢迎&#xff1f;” 来我和我的朋友/同事之间。 另外&#xff0c;我确实收到了许多人发来的电子邮件&#xff0c;询问“春天很大&#xff0c;如何快速学习&#xff1f;” 。 在这篇文章中&…

Java实现复数Complex的加减乘除运算、取模、求幅角角度

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; /*** Author: Yeman* Date: 2021-09-23-9:03* Description:*/…

Java集合(8)--集合工具类Collections

Collections 是一个操作 Set、List 和 Map 等集合的工具类。 Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作&#xff0c;还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。 排序操作 reverse(List)&#xff1a;反转 List 中元素的顺…

Java泛型(1)--集合使用泛型Generic、自定义泛型、泛型在继承上的体现、通配符的使用

文章目录泛型的概念集合中使用泛型自定义泛型结构泛型在继承上的体现通配符的使用泛型的概念 集合容器类在设计阶段/声明阶段不能确定这个容器实际存的是什么类型的对象&#xff0c;所以在JDK1.5之前只能把元素类型设计为Object&#xff0c;JDK1.5之后使用泛型来解决。因为这个…

蚂蚁组件 axure 蚂蚁_蚂蚁属性细微差别

蚂蚁组件 axure 蚂蚁每隔一段时间&#xff0c;我会想起Ant属性的一些细微差别 &#xff0c;一旦忘记它们&#xff0c;在与Ant交互时会引起混乱。 特别是&#xff0c; Ant属性 通常是不可变的 &#xff08;不包括Ant 1.8以来的局部属性 &#xff09;&#xff0c;并且在其第一次设…