第一:什么是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 鉴权的更多相关文章

  1. Spring Boot认证:整合Jwt

    背景 Jwt全称是:json web token.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证. 优点 简洁: 可以通过 ...

  2. Spring Boot 鉴权之—— springboot2.0.4+mybatis 整合的完整用例

    自上一篇文章的基础上,Spring Boot 鉴权之—— JWT 鉴权我做了一波springboot2.0.4+mybatis 的整合. 参考文章: Spring Boot+Spring Securi ...

  3. Spring Boot 知识图谱

    最近有意重新学习下SpringBoot知识,特地总结了SpringBoot的知识点,对于想要学习的人来说,够用. SpringBoot学习路径 第一部分:了解 Spring Boot Spring B ...

  4. Spring Boot整合实战Spring Security JWT权限鉴权系统

    目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以.像以前做项目的安全认证基于 session 的登录拦截,属于后端全栈式的开发的模式 ...

  5. spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法

    spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...

  6. HTTP基本认证和JWT鉴权

    一.HTTP基本认证 Basic Authentication——当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: · 客户端发送http请求 ·  ...

  7. JWT鉴权

    一.HTTP基本认证 Basic Authentication--当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: 客户端发送http请求 服务器发 ...

  8. jwt鉴权学习 (php示例代码)

    前段时间听朋友讲起 jwt鉴权,博主我是一脸懵逼,通过朋友坚持不懈的讲解,我终于听懂了,jwt就是登陆token校验嘛 然而事情并不是博主想象的那么简单,在一个艳阳高照,晴空万里的夜晚,博主手贱百度了 ...

  9. Shiro(4)默认鉴权与自定义鉴权

    =========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...

随机推荐

  1. 2019年java后端年终总结(六年开发经验),送给正在努力的你

    长大之后,时间总是过得飞快,转眼之间,今年已经只剩下1天了.小时候总感觉遥不可及.只在科幻小说里面出现的2020年,已经开始进入蓄力期了. 这篇文章主要和大家聊一聊分析2019年java技术的更新给大 ...

  2. visual studio2019下动态链接库的制作

    打开visual studio2019创建动态链接库项目,项目名称为20199324dll 然后定义宏:在头文件中定义即可,宏的作用的是允许该函数能够被外部访问,并直接调用.代码如下: // pch. ...

  3. C#匿名委托,匿名函数,lambda表达式

    一.类型.变量.实例之间的关系. 类型>变量>实例 类型可以创建变量,实体类可以创建实例,实例可以存储在变量里. 二.委托使用过程: 1.定义委托(写好签名): 2.创建委托变量: 3.给 ...

  4. C#.NET中的ToString()数字格式化

    数字格式字符串-----货币-----.ToString("C");.ToString("c");例 2.5.ToString("c") - ...

  5. 分辨率单位及换算,LW / PH、LP / mm、L / mm、Cycles / mm、Cycles / pixel、LP / PH

    对于测试数字成像设备非常重要的参数是分辨率.但有不同的方式来表达数码相机的分辨率,有时令人困惑.下面介绍的是是最常见的单位. 百万像素 “这台相机有1000万像素的分辨率”是我们经常在广告上看到的数据 ...

  6. TPO4-2 Cave Art in Europe

    Perhaps, like many contemporary peoples, Upper Paleolithic men and women believed that the drawing o ...

  7. 关于XML的一些总结

    xml的知识结构图 eXtensible Markup Language,可扩展标记语言,简称XML,和HTML比较而言,语法相似,作用不同 XML被设计用来存储和传输数据,但存储数据方面,数据库是主 ...

  8. hm nsis edit请求的操作需要提升

    第一次用nsis做安装包,编译运行出现这个问题,解决办法:管理员身份运行即可

  9. [Algo] 281. Remove Spaces

    Given a string, remove all leading/trailing/duplicated empty spaces. Assumptions: The given string i ...

  10. 关于jQuery中toggle参数callback函数提前执行问题

    通过 jQuery,您可以使用 toggle() 方法来切换 hide() 和 show() 方法. 显示被隐藏的元素,并隐藏已显示的元素: $(selector).toggle(speed,call ...