SpringSecurity 整合 JWT
在上一篇基础上继续集成 JWT ,实现用户身份验证。
前言
前后端分离项目中,如果直接把 API 接口对外开放,我们知道这样风险是很大的,所以在上一篇中我们引入了 Spring Security ,但是我们在登陆后缺少了请求凭证部分。
什么是JWT?
JWT是 Json Web Token 的缩写。它是基于 RFC 7519 标准定义的一种可以安全传输的 小巧 和 自包含 的JSON对象。由于数据是使用数字签名的,所以是可信任的和安全的。JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名。
JWT的工作流程
1、用户进入登录页,输入用户名、密码,进行登录;
2、服务器验证登录鉴权,如果改用户合法,根据用户的信息和服务器的规则生成 JWT Token
3、服务器将该 token 以 json 形式返回(不一定要json形式,这里说的是一种常见的做法)
4、用户得到 token,存在 localStorage、cookie 或其它数据存储形式中。以后用户请求 /protected 中的 API 时,在请求的 header 中加入 Authorization: Bearer xxxx(token)。此处注意token之前有一个7字符长度的 Bearer。
5、服务器端对此 token 进行检验,如果合法就解析其中内容,根据其拥有的权限和自己的业务逻辑给出对应的响应结果。
6、用户取得结果
如下如所示:
7790cc3aade467c985e2e4a8105b89f1.png
来看一下 JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
token 分成了三部分,头部(header),荷载(Payload) 和 签名(Signature),每部分用 . 分隔,其中头部和荷载使用了base64编码,分别解码之后得到两个JSON串:
第一部分-头部:
{
"alg": "HS256",
"typ": "JWT"
}
alg字段为加密算法,这是告诉我们 HMAC 采用 HS512 算法对 JWT 进行的签名。
第二部分-荷载:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
荷载的字段及含义:
- iss: 该JWT的签发者
- sub: 该JWT所面向的用户
- aud: 接收该JWT的一方
- exp(expires): 什么时候过期,这里是一个Unix时间戳
- iat(issued at): 在什么时候签发的
这段告诉我们这个Token中含有的数据声明(Claim),这个例子里面有三个声明:sub, name 和 iat。在我们这个例子中,分别代表着
所面向的用户、用户名、创建时间,当然你可以把任意数据声明在这里。
第三部分-签名:
第三部分签名则不能使用base64解码出来,该部分用于验证头部和荷载数据的完整性。
JWT的生成和解析
引入依赖:
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
创建一个测试类尝试一下 JWT 的生成:
public class Test {
public static void main(String[] args){
String token = Jwts.builder()
主题 放入用户名
.setSubject("niceyoo")
自定义属性 放入用户拥有请求权限
.claim("authorities","admin")
失效时间
.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))
签名算法和密钥
.signWith(SignatureAlgorithm.HS512, "tmax")
.compact();
System.out.println(token);
}
}
控制台打印如下:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1ODM1M30.keCiHrcEr0IWXfZLocgHS8znn7uSiaZW1IT6bTs-EQG0NPsb6-Aw_XbGQea4mez2CcAflgMqtzIpsDjZsUOVug
数据声明(Claim)是一个自定义属性,可以用来放入用户拥有请求权限。上边为简单直接传了一个 'admin'。
再看看解析:
public static void main(String[] args){
try {
解析token
Claims claims = Jwts.parser()
.setSigningKey("tmax")
.parseClaimsJws("eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1OTc2Mn0.MkSJtGaVePLa-eM3gylh1T3fwODg-6ceDDOxscXAQKun-qNrbQFcKPNqXhblbXPNLhaJyEnwugNANCTs98UNmA")
.getBody();
System.out.println(claims);
获取用户名
String username = claims.getSubject();
System.out.println("username:"+username);
获取权限
String authority = claims.get("authorities").toString();
System.out.println("权限:"+authority);
} catch (ExpiredJwtException e) {
System.out.println("jwt异常");
} catch (Exception e){
System.out.println("异常");
}
}
控制台打印:
{sub=niceyoo, authorities=admin, exp=1559459762}
username:niceyoo
权限:admin
JWT 本身没啥难度,但安全整体是一个比较复杂的事情,JWT 只不过提供了一种基于 token 的请求验证机制。但我们的用户权限,对于 API 的权限划分、资源的权限划分,用户的验证等等都不是JWT负责的。也就是说,请求验证后,你是否有权限看对应的内容是由你的用户角色决定的。所接下来才是我们的重点,Spring Security 整合 JWT。
集成JWT
要想要 JW T在 Spring 中工作,我们应该新建一个 JWT filter,并把它配置在 WebSecurityConfig 中。
WebSecurityConfigurerAdapter.java
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailHandler failHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());加密
}
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
.authorizeRequests();
registry.and()
表单登录方式
.formLogin()
.permitAll()
成功处理类
.successHandler(successHandler)
失败
.failureHandler(failHandler)
.and()
.logout()
.permitAll()
.and()
.authorizeRequests()
任何请求
.anyRequest()
需要身份认证
.authenticated()
.and()
关闭跨站请求防护
.csrf().disable()
前后端分离采用JWT 不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
添加JWT过滤器 除已配置的其它请求都需经过此过滤器
.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));
}
}
相较于上一篇主要多了如下一行配置:
.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));
JWTAuthenticationFilter.java
@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
private Integer tokenExpireTime;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, Integer tokenExpireTime) {
super(authenticationManager);
this.tokenExpireTime = tokenExpireTime;
}
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
super(authenticationManager, authenticationEntryPoint);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(SecurityConstant.HEADER);
if(StrUtil.isBlank(header)){
header = request.getParameter(SecurityConstant.HEADER);
}
Boolean notValid = StrUtil.isBlank(header) || (!header.startsWith(SecurityConstant.TOKEN_SPLIT));
if (notValid) {
chain.doFilter(request, response);
return;
}
try {
UsernamePasswordAuthenticationToken 继承 AbstractAuthenticationToken 实现 Authentication
所以当在页面中输入用户名和密码之后首先会进入到 UsernamePasswordAuthenticationToken验证(Authentication),
UsernamePasswordAuthenticationToken authentication = getAuthentication(header, response);
SecurityContextHolder.getContext().setAuthentication(authentication);
}catch (Exception e){
e.toString();
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(String header, HttpServletResponse response) {
用户名
String username = null;
权限
List<GrantedAuthority> authorities = new ArrayList<>();
try {
解析token
Claims claims = Jwts.parser()
.setSigningKey(SecurityConstant.JWT_SIGN_KEY)
.parseClaimsJws(header.replace(SecurityConstant.TOKEN_SPLIT, ""))
.getBody();
logger.info("claims:"+claims);
获取用户名
username = claims.getSubject();
logger.info("username:"+username);
获取权限
String authority = claims.get(SecurityConstant.AUTHORITIES).toString();
logger.info("authority:"+authority);
if(!StringUtils.isEmpty(authority)){
authorities.add(new SimpleGrantedAuthority(authority));
}
} catch (ExpiredJwtException e) {
ResponseUtil.out(response, ResponseUtil.resultMap(false,401,"登录已失效,请重新登录"));
} catch (Exception e){
log.error(e.toString());
ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"解析token错误"));
}
if(StrUtil.isNotBlank(username)) {
踩坑提醒 此处password不能为null
User principal = new User(username, "", authorities);
return new UsernamePasswordAuthenticationToken(principal, null, authorities);
}
return null;
}
}
接下来我们启动项目看看:
访问项目中已有的链接:
http://localhost:7777/tmax/videoCategory/getAll
老样子认证一波:
其中 niceyoo、 为数据库用户信息
登陆成功后获取返回的 token,注意,此 token 是由 JWT 生成的:
String token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()
主题 放入用户名
.setSubject(username)
自定义属性 放入用户拥有请求权限
.claim(SecurityConstant.AUTHORITIES, authorities)
失效时间
.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))
签名算法和密钥
.signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY)
.compact();
浏览器返回 token 如下:
ad45accf0b31c606a10c568acbddec19.png
然后我们通过 token 凭证去访问上边的方法:
e3c05b207e1ec11e1f23560ef1b724b6.png
后台打印信息:
claims:{sub=niceyoo, authorities=admin, exp=1559472866}
username:niceyoo
authority:admin
随便改一下 token ,返回如下:
SpringSecurity 整合 JWT的更多相关文章
- SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- SpringSecurity整合JWT
一.前言 最近负责支付宝小程序后端项目设计,这里主要分享一下用户会话.接口鉴权的设计.参考过微信小程序后端的设计,会话需要依靠redis.相关的开发人员和我说依靠Redis并不是很靠谱,redis在业 ...
- 【SpringBoot技术专题】「JWT技术专区」SpringSecurity整合JWT授权和认证实现
JWT基本概念 JWT,即 JSON Web Tokens(RFC 7519),是一个广泛用于验证 REST APIs 的标准.虽说是一个新兴技术,但它却得以迅速流行. JWT的验证过程是: 前端(客 ...
- SpringBoot整合SpringSecurity实现JWT认证
目录 前言 目录 1.创建SpringBoot工程 2.导入SpringSecurity与JWT的相关依赖 3.定义SpringSecurity需要的基础处理类 4. 构建JWT token工具类 5 ...
- SpringSecurity之整合JWT
SpringSecurity之整合JWT 目录 SpringSecurity之整合JWT 1. 写在前面的话 2. JWT依赖以及工具类的编写 3. JWT过滤器 4. 登录成功结果处理器 5. Sp ...
- 基于SpringSecurity和JWT的用户访问认证和授权
发布时间:2018-12-03 技术:springsecurity+jwt+java+jpa+mysql+mysql workBench 概述 基于SpringSecurity和JWT的用户访 ...
- Spring Security整合JWT,实现单点登录,So Easy~!
前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况 ...
- SpringBoot + SpringSecurity + Mybatis-Plus + JWT实现分布式系统认证和授权
1. 简介 Spring Security是一个功能强大且易于扩展的安全框架,主要用于为Java程序提供用户认证(Authentication)和用户授权(Authorization)功能. ...
- SpringBoot + SpringSecurity + Mybatis-Plus + JWT + Redis 实现分布式系统认证和授权(刷新Token和Token黑名单)
1. 前提 本文在基于SpringBoot整合SpringSecurity实现JWT的前提中添加刷新Token以及添加Token黑名单.在浏览之前,请查看博客: SpringBoot + Sp ...
随机推荐
- SGU 128. Snake --- 暴力枚举+并查集+贪心+计算几何
<传送门> 128. Snake time limit per test: 0.25 sec. memory limit per test: 4096 KB There are N poi ...
- 个人学习笔记:C语言程序结构
个人笔记:C语言程序 函数 语句 输入输出对象 标识符 关键字 函数 一个C语言源程序,是由一个或多个函数定义顺序组成的,其中必须有一个函数名为main的主函数.C语言源程序中的函数是指完成特定数据处 ...
- STVD生成hex,bin,显示ram&flash的使用情况
前言: 虽然stvd免费,但使用起来并不令人满意,不能自动补全,界面丑陋,设置繁琐,最难受的是不会自动输出ram和flash的使用情况.当然方法还是有的,下面就讲讲我是怎么实现的.个人水平有限,如有错 ...
- 通过分析 WPF 的渲染脏区优化渲染性能
原文:通过分析 WPF 的渲染脏区优化渲染性能 本文介绍通过发现渲染脏区来提高渲染性能. 本文内容 脏区 Dirty Region WPF 性能套件 脏区监视 优化脏区重绘 脏区 Dirty Regi ...
- 给自己看的Cache,三段代码
此篇是我记录代码的一个草稿,不是一篇正式的博文,误点的别介意啊. 公司的框架中Cache实现文件: (1)CacheUtil.cs using System.Collections.Generic; ...
- sql servse 常用维护sql
1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- 创建 备份 ...
- 一个多进程爬虫下载图片的demo
import os,re import pickle import requests import random import time from bs4 import BeautifulSoup f ...
- 又一个秘密如何让浏览器访问最新的js,css等外部引用
在引用文件末尾加上一个参数,让浏览器知道这个文件跟上一个文件是不同的,让浏览器去服务器重新加载最新的,例如:<script type="text/javascript" sr ...
- GridPanel列头带有复选框的列
由于工作需要,封装了ExtJS4,GridPanel列头带有复选框的列, 代码如下: /** * 列头带有复选框的列 * */ Ext.define("org.pine.widget.Che ...
- vue-quill-edito 字体倾斜加粗无效
长话短说,出现这种情况的原因80%-90%的概率在你项目里面有一个全局的 一般在reset.css重置文件中 font-weight:normal; font-style:normal; font-s ...