第一:什么是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. Linux系统如何记录时间

    1.内核在开机启动的时候会读取RTC硬件获取一个时间作为初始基准时间,这个基准时间对应一个jiiffies值(这个基准时间换算成jiffies值的方法是:用这个时间减去1970-01-01  00:0 ...

  2. Python字符串与列表

  3. Codeforces Round #556(Div.1)

    A 容易发现i,i+1至少有一个数出现,于是可以让尽量多的2和奇数出现 #include<bits/stdc++.h> using namespace std; int n,s1,s2; ...

  4. yum源本地部署完后网络部署报错

    错误信息 已加载插件:fastestmirror Determining fastest mirrors * base: mirrors.aliyun.com * extras: mirrors.al ...

  5. sql语句查询去除重复语句的结果集

    返回operation表中time列的唯一值 语句1 select  time  from  operation   group  by  time   having count(time) > ...

  6. vue实现动态绑定class--(三目运算符)根据span数字内容改变其样式

    一.根据span数字内容改变数字本身样式(两种样式) <template> //使用三目运算符,判断当span的val是否小于0给其不同的class名 <span class=&qu ...

  7. Memcache工作原理

    1       Memcache是什么 Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的 ...

  8. Hadoop_ssh免密认证出现的问题

    问题 network is unreachable 网络解决方法 看了大部分文章博客,发现都是说网关或者没ping好,我都试了,发现没用 我的解决方法 把虚拟机右上角的网络连接即可(我的是S开头的一个 ...

  9. Leetcode1_两数之和

    题目 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同 ...

  10. Node.js //TODO

    目录 技术背景 开发环境 学习过程 参考资料 结束语 技术背景 开发环境 学习过程 参考资料 结束语