头图

Json Web token

通过json形式作为Web应用的令牌,用于各方之间安全的将信息作为JSON对象传送,在传输过程中可以完成数据加密,签名等处理

JWT能做什么

1、授权

一但用户登录,每个后续请求都会包括JWT,从而允许用户访问该令牌允许的路由,服务和资源 单点登录就是JWT实现的 开销小,再不同域中可以轻松访问

2、信息交换

JWT可以签名确保数据发件人的正确性,由于签名是使用标头和有效负载计算的,因此我们还可以验证内容是否遭到篡改。

基于传统的Session认证

http协议本身是无状态的,如果用户向我们提供用户名和密码进行认证后,下一次登录还是需要用户提供认证信息。因为我们并不知道是那个用户发出的请求。

当用户认证成功后我们在服务器创建一个会话,也就是一个Session,然后把用户信息保存到Session中  然后服务器会返回一个Sessionid的 cookie。
那么用户之后每次登录都会带着自己的Sessionid 那么如果服务器有就证明我们之前认证过。

问题

1、每个用户认证后都会在服务器上存储一个Session,这个东西一般是在内存中的,用户增多服务器开销太大

2、一个Session是存在一个服务器上的,使得分布式应用没有意义

3、cookie如果被截获,用户就饿会很容易受到跨站请求伪造攻击

4、在前后端分离的项目中及其痛苦,

JWT认证过程

首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。

后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload (负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。形成的JWT就是一个形同111.ZZZ.xxx的字符串。token head.payload.singurater

后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localstorage或sessionStorage上,退出登录时前端删除保存的JWT即可。

前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF(问题) HEADER axios h

后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确﹔检查Token是否过期;检查Token的接收方是否是自己(可选)。
–验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

JWT结构

token其实就是一个string 但是是三段header.payload.signature

1、标头(Header) 通常由两部分组成 令牌的类型(JWT)和所使用的签名算法,然后使用Base64编码组成JWT的结构的第一部分

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

2、有效载荷(Payload) 其中包含声明,声明是有关实体(通常是用户)和其他数据的声明,同样也会用Base64编码组成JWT的第二部分 注意不要放敏感信息不安全

{
    "name":"shuaikb",
    "admin":true
}

3、签名(Signature) 签名需要使用编码后的header和payload以及我们提供的一个随机密钥,然后使用header中指定的签名算法进行签名。签名的作用时保证JWT没有被篡改过。

签名的目的:实际上时对头部已经负载内容进行标记,防止被篡改。

因为Base64是可逆的所以不要在 负载中放任何敏感信息。

jwt的应用

依赖导入
<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.1</version>
</dependency>

JWT的生成

 @Test
    public void test(){
        HashMap<String, Object> map = new HashMap<>();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,60);
        String token = JWT.create()
                .withHeader(map)//header
                .withClaim("userid",21)//payload
                .withClaim("username","shuaikb")
                .withExpiresAt(instance.getTime())//过期时间
                .sign(Algorithm.HMAC256("DuoShuaio_Shuaikb"));//签名
        System.out.println(token);
}

//进阶写法
   private static final String  Signature = "DuoShuaio_Shuaikb";
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);

        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        String token = builder.withExpiresAt(instance.getTime())
                .sign(Algorithm.HMAC256(Signature));
        
        return token;
}

token验证

//验证对象
        JWTVerifier duoShuaio_shuaikb = JWT.require(Algorithm.HMAC256("DuoShuaio_Shuaikb")).build();
        DecodedJWT verify = duoShuaio_shuaikb.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MjkzOTA0ODMsInVzZXJpZCI6MjEsInVzZXJuYW1lIjoic2h1YWlrYiJ9.ezfUFdZ9BpeOBI0drrl2VE2HceX2znyjDc4H2Iwhsx8");
       System.out.println(verify.getClaim("userid").asInt());
        System.out.println(verify.getClaim("username").asString());

//必须再token有效期才能拿到

接口测试

@Data
public class YongHu {
    private int id;
    private String name;
    private String password;
}


@Repository
public interface YongHuDao {
    YongHu login(YongHu yongHu);
}


public interface YongHuService {

    YongHu login(YongHu yongHu);

}



@Service
@Transactional
public class YongHuServiceImpl implements YongHuService{

    @Autowired
    private YongHuDao YONG_HU_DAO;


    @Override
    public YongHu login(YongHu yongHu){
        YongHu yongHu1 = YONG_HU_DAO.login(yongHu);
        if (yongHu1!=null){
            return yongHu1;
        }
        throw new RuntimeException("登录失败");
    }
}


@RestController
@Slf4j
public class YongHuController {

    @Autowired
    private YongHuService yongHuService;

    @GetMapping("/login")
    public Map<String,Object> login(YongHu yongHu) {
        log.info("用户名:[{}]",yongHu.getName());
        log.info("密码:[{}]",yongHu.getPassword());
        Map<String,Object> map = new HashMap<>();
        try {
            YongHu yongHu1 = yongHuService.login(yongHu);
            Map<String,String> payload = new HashMap<>();
            payload.put("id", String.valueOf(yongHu1.getId()));
            payload.put("name", yongHu1.getName());
            //生成JWT令牌
            String token = JwtUtils.getToken(payload);
            map.put("token",token);
            map.put("state",true);
            map.put("msg","认证成功");
        }catch (Exception e){
            map.put("state",false);
            map.put("msg",e.getMessage());
        }
        return  map;
    }

    @PostMapping("/test")
    public Map<String,Object> test(String token){
        log.info("当前token为:[{}]",token);
        Map<String,Object> map =new HashMap<>();
        //验证令牌
        try{
            JwtUtils.verify(token);
            map.put("state",true);
            map.put("msg","请求成功");
            return map;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","Token过期");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","签名算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","Token无效");
        }
        map.put("state",false);
        return map;
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shuaikb.dao.YongHuDao">

    <select id="login" parameterType="YongHu" resultType="YongHu">
        select * from jwt where name=#{name} and password=#{password}
    </select>

</mapper>

问题就出现了如果每一个接口我们都要去写一边token验证,那代码就太冗余了。所以我们可以选择AOP 写一个拦截器 springcloud就写在网关里。

拦截器

package com.shuaikb.interceptors;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.shuaikb.utils.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Shuaikb
 * @create 2021/8/20 17:44
 */
public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        Map<String,Object> map = new HashMap<>();
        try{
            JwtUtils.verify(token);
            map.put("state",true);
            map.put("msg","请求成功");
            return true;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","Token过期");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","签名算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","Token无效");
        }
        map.put("state",false);
        //想应到前台为json
        //resposebody底层 是用的jackson
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}

配置类(过滤器)

package com.shuaikb.config;

import com.shuaikb.interceptors.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author Shuaikb
 * @create 2021/8/20 17:52
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/yonghu/test")
                .excludePathPatterns("/yonghu/login");
    }
}

数据验证

@PostMapping("/test")
public Map<String,Object> test(HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        String token = request.getHeader("token");
        DecodedJWT verify = JwtUtils.verify(token);
        log.info("用户id:[{}]:",verify.getClaim("id").asString());
        log.info("用户name:[{}]",verify.getClaim("name").asString());
        map.put("test","成功");
        return map;
}

Shuaikb
1 声望0 粉丝