Spring Boot 鉴权之—— JWT 鉴权
第一:什么是JWT鉴权
1. JWT即JSON Web Tokens,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),他可以用来安全的传递信息,因为传递的信息是经过加密算法加密过得。
2.JWT常用的加密算法有:HMAC算法或者是RSA的公私秘钥对进行签名,也可以使用公钥/私钥的非对称算法
3.JWT的使用场景主要包括:
1) 认证授权,特别适用于分布式站点的单点登录(SSO)场景,只要用户开放的登录入口登录过一次系统,就会返回一个token,之后的请求都需要包含token。
2)交换信息,通过使用密钥对来安全的传送信息,可以知道发送者是谁、放置消息是否被篡改,一般被用来在身份提供者和服务提供者之间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,例如:设备信息,版本号等,该token也可直接被用于认证,也可被加密。
第二:JWT构成
JSON Web Tokens(JWT)有三部分构成,用英文句点分割(.) ,一般看起来例如:xxxxx.yyyyy.zzzzz
分为:
Header 头信息
Payload 荷载信息,实际数据
Signature 由头信息+荷载信息+密钥 组合之后进行加密得到
1) Header 头信息通常包含两部分,type:代表token的类型,这里使用的是JWT类型。 alg:代表使用的算法,例如HMAC SHA256或RSA.
{
"alg": "HS256",
"typ": "JWT"
} // 这会被经过base64Url编码形成第一部分
2)Payload 一个token的第二部分是荷载信息,它包含一些声明Claim(实体的描述,例:用户信息和其他的一些元数据)
声明分三类:
1)Reserved Claims,这是一套预定义的声明,并不是必须的,这是一套易于使用、操作性强的声明。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等
2)Plubic Claims,
3)Private Claims,交换信息的双方自定义的声明
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}//同样经过Base64Url编码后形成第二部分
3) signature 使用header中指定的算法将编码后的header、编码后的payload、一个secret进行加密
例如使用的是HMAC SHA256算法,大致流程类似于: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
这个signature字段被用来确认JWT信息的发送者是谁,并保证信息没有被修改
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
) secret base64 encoded
第三步:JWT认证流程
上图是官方提供的一个认证流程图 ,我们可以看到它的授权流程是:
1.客户端通过post请求请求服务端登录认证接口
2.服务端用秘密创建JWT
3.服务端将JWT返回浏览器
4.客户端在授权报头上发送JWT
5.服务端检查JWT签名从JWT获取用户信息
6.服务端向客户端发送响应
通常我们所看到的认证流程,只能看到第一步和第六步,如果使用调试模式或者用抓包工具抓取就可以看到完整流程。
第四步:jwt使用
源码地址:
github: https://github.com/GitHubZhangCom/spring-security-oauth-example/
码云:https://gitee.com/region/spring-security-oauth-example/tree/master/spring-jwt
1)、 引入相关jar:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency> <!-- 使用lombok优雅的编码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2)、jwt相关编程
JwtUtil:jwt工具类
import org.springframework.util.StringUtils; /**
* jwt工具类
* @author zyl
*
*/
public class JwtUtils {
private static final String AUTHORIZATION_HEADER_PREFIX = "Bearer "; /**
* 获取原始令牌
* remove 'Bearer ' string
*
* @param authorizationHeader
* @return
*/
public static String getRawToken(String authorizationHeader) {
return authorizationHeader.substring(AUTHORIZATION_HEADER_PREFIX.length());
} /**
* 获取令牌头
* @param rawToken
* @return
*/
public static String getTokenHeader(String rawToken) {
return AUTHORIZATION_HEADER_PREFIX + rawToken;
} /**
* 验证授权请求头
* @param authorizationHeader
* @return
*/
public static boolean validate(String authorizationHeader) {
return StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_HEADER_PREFIX);
} /**
* 获取授权头前缀
* @return
*/
public static String getAuthorizationHeaderPrefix() {
return AUTHORIZATION_HEADER_PREFIX;
}
}
JwtAuthenticationFilter
import java.io.IOException;
import java.util.ArrayList; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import com.jwt.server.util.JwtUtils; import io.jsonwebtoken.Jwts; /**
* 自定义JWT认证过滤器
* 该类继承自BasicAuthenticationFilter,在doFilterInternal方法中,
* 从http头的Authorization 项读取token数据,然后用Jwts包提供的方法校验token的合法性。
* 如果校验通过,就认为这是一个取得授权的合法请求
* @author zyl
*
*/
public class JwtAuthenticationFilter extends BasicAuthenticationFilter { public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
} @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader("Authorization"); if (header == null || !header.startsWith(JwtUtils.getAuthorizationHeaderPrefix())) {
chain.doFilter(request, response);
return;
} UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(header); SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
} private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
String user = Jwts.parser()
.setSigningKey("PrivateSecret")
.parseClaimsJws(token.replace(JwtUtils.getAuthorizationHeaderPrefix(), ""))
.getBody()
.getSubject(); if (null != user) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
} return null;
}
}
JwtLoginFilter
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.fasterxml.jackson.databind.ObjectMapper;
import com.jwt.server.domain.UserInfo;
import com.jwt.server.util.JwtUtils; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; /**
* 验证用户名密码正确后,生成一个token,并将token返回给客户端
* 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法 attemptAuthentication
* :接收并解析用户凭证。 successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成token。
*
*/
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; public JwtLoginFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
} @Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
UserInfo user = new ObjectMapper().readValue(request.getInputStream(), UserInfo.class); return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
} // 用户成功登录后,这个方法会被调用,我们在这个方法里生成token
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
String token = Jwts.builder().setSubject(((User) authResult.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(SignatureAlgorithm.HS512, "PrivateSecret").compact();
response.addHeader("Authorization", JwtUtils.getTokenHeader(token));
} }
SecurityConfiguration config配置
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import com.jwt.server.filter.JwtAuthenticationFilter;
import com.jwt.server.filter.JwtLoginFilter; /**
* 通过SpringSecurity的配置,将JWTLoginFilter,JWTAuthenticationFilter组合在一起
*
* @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 在springboot1.5.8的时候该注解是可以用的 具体看源码
* @author zyl
*
*/
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
} @Override
protected void configure(HttpSecurity http) throws Exception { //自定义 默认
http.cors().and().csrf().disable().authorizeRequests() .antMatchers("/user/login","/login", "/oauth/authorize").permitAll()
.anyRequest().authenticated()
.and()
.requestMatchers().antMatchers("/user/login","/login","/oauth/authorize")
.and()
.addFilter(new JwtLoginFilter(authenticationManager()))//登录过滤器
.addFilter(new JwtAuthenticationFilter(authenticationManager()));//自定义过滤器 } }
UserInfo 认证用户
import lombok.Data; /**
* 认证用户
* @author zyl
*
*/
@Data
public class UserInfo { private String id;
private String username;
private String password; public UserInfo() {
this.setId("testId");
this.setUsername("testUsername");
this.setPassword("testPassword");
}
}
UserDetailServiceImpl:核心认证用户service类
import static java.util.Collections.emptyList; import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import com.jwt.server.domain.UserInfo; /**
*
* @author zyl
*
*/
@Service
public class UserDetailServiceImpl implements UserDetailsService { @Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
UserInfo user = new UserInfo();
return new User(user.getUsername(), user.getPassword(), emptyList());
}
}
第五步:测试
请求登录:localhost:8085/login
这里测试登录的是请用post方式,因为默认源码里边只支持post方式
自定义登录:localhost:8085/user/login
Spring Boot 鉴权之—— JWT 鉴权的更多相关文章
- Spring Boot认证:整合Jwt
背景 Jwt全称是:json web token.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证. 优点 简洁: 可以通过 ...
- Spring Boot 鉴权之—— springboot2.0.4+mybatis 整合的完整用例
自上一篇文章的基础上,Spring Boot 鉴权之—— JWT 鉴权我做了一波springboot2.0.4+mybatis 的整合. 参考文章: Spring Boot+Spring Securi ...
- Spring Boot 知识图谱
最近有意重新学习下SpringBoot知识,特地总结了SpringBoot的知识点,对于想要学习的人来说,够用. SpringBoot学习路径 第一部分:了解 Spring Boot Spring B ...
- Spring Boot整合实战Spring Security JWT权限鉴权系统
目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以.像以前做项目的安全认证基于 session 的登录拦截,属于后端全栈式的开发的模式 ...
- spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法
spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...
- HTTP基本认证和JWT鉴权
一.HTTP基本认证 Basic Authentication——当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: · 客户端发送http请求 · ...
- JWT鉴权
一.HTTP基本认证 Basic Authentication--当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: 客户端发送http请求 服务器发 ...
- jwt鉴权学习 (php示例代码)
前段时间听朋友讲起 jwt鉴权,博主我是一脸懵逼,通过朋友坚持不懈的讲解,我终于听懂了,jwt就是登陆token校验嘛 然而事情并不是博主想象的那么简单,在一个艳阳高照,晴空万里的夜晚,博主手贱百度了 ...
- Shiro(4)默认鉴权与自定义鉴权
=========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...
随机推荐
- zeroc ice log4net 多进程log文件问题
使用zeroc ice 中的icebox 的时候多服务会有多个服务实例,每个实例都要写日志文件,所以要配置多个日志文件区分开来, 类似这样 orderservice_1_20160101.log ...
- Springboot配置注入
springboot中如何将yml 配置文件中配置,在类中注入使用 ①第一种使用@value方式 ################## 配置文件 ######################### d ...
- Django数据迁移时提示 ModuleNotFoundError: No module named 'users'
执行数据迁移时提示找不到对应的APP,错误如下: 这个错误主要是路径找不到引起的,只需在settings文件夹中添加app文件路径即可 sys.path.insert(0, os.path.join( ...
- Django模型迁移提示版本不匹配解决办法
Django迁移模型时提示django.core.exceptions.ImproperlyConfigured:mysqlclient 1.3.7 or newer is required; you ...
- MVP简要示例
MVP即Model-View-Presenter,是从经典的MVC演变而来的,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理.Model提供数据.View负责显 ...
- 吴裕雄--天生自然python学习笔记:python安装配置tesseract-ocr-setup-3.05.00dev.exe
下载地址:https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-setup-3.05.00dev.exe 点击安装,记得复制安装的路径,待会 ...
- Linux/UNIX 上安装 MySQL
Linux/UNIX 上安装 MySQL Linux平台上推荐使用RPM包来安装Mysql,MySQL AB提供了以下RPM包的下载地址: MySQL - MySQL服务器.你需要该选项,除非你只想连 ...
- day12-模块导入
# 一.import import demo # in demo.py -- 导入demo模块,执行里面的print语句. print(demo.money) # 8000000 -- 打印demo的 ...
- 各大厂RTSP取流的URI
目前使用过的各大厂商取流规则是在实际的工作中遇到的相关视频接入问题,通过rtsp协议接入视频数据的一些记录,其中的图片可能来源于网络,内容部分来源于网络,本人仅仅是对相关内容作了汇总. 海康RTSP取 ...
- easyui 表单提交前的 confirm 处理
最近学习用 easyui,异步提交表单是遇到一个小问题 $('#fModiDetail').form('submit',{ url:'...', onSubmit:function(){ if($(t ...