批里批里 (゜-゜)つ🍺 干杯~|

七つ一旋桜

园龄:4年2个月粉丝:6关注:3

2021-10-11 12:51阅读: 125评论: 0推荐: 0

jwt学习笔记

jwt是json web token的缩写,常用于安全校验

jwt的组成

jwt有三部分组成,用.拼接

这三部分是

  • Header(数据类型和加密算法类型)

    {
        'typ': 'JWT',
        'alg': 'HS256'
    }
    
  • Payload (存放有效信息)

    {
        'sub': '1234567890',
        'name': 'john',
        'admin': true
    }
    
  • signatrue 对前两个部分进行再编码

    var encodeString = base64UrlEncode(header) + '.' + 
    base64UrlEncode(payload)
    
    var signatrue = HMACSHA256(encodedString, 'secret')
    

依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

创建一个jwt

package zjc;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.UUID;

public class Test {
	private long time = 1000 * 60 * 60 * 24;
	private String signature = "dasdia";

	@org.junit.Test
	public void test() {
		JwtBuilder jwtBuilder = Jwts.builder(); // 构建jwt对象
		String token = jwtBuilder
						// header
						.setHeaderParam("typ", "JWT")
						.setHeaderParam("alg", "HS256")
						// payload
						.claim("username", "张三")
						.claim("role", "admin")
						.setSubject("admin-test")
						.setExpiration(new Date(System.currentTimeMillis() + time)) // 有效期
						.setId(UUID.randomUUID().toString())
						// signature
						.signWith(SignatureAlgorithm.HS256, signature)
						.compact();
		System.out.println(token);
	}
}

运行获得以下token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTU0MDgsImp0aSI6IjRjMWJiZGZlLTRkNzMtNDJmOS05NzE3LTZjOWE5NGI3NTBhNSJ9.CcDS6k2vcfz_ajUlhHtGhJEm_VzzohzLXJaAIylB-Eo

同样的,有token和signature就可以从token中获取数据

@org.junit.Test
    public void parse() {
    String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTU0MDgsImp0aSI6IjRjMWJiZGZlLTRkNzMtNDJmOS05NzE3LTZjOWE5NGI3NTBhNSJ9.CcDS6k2vcfz_ajUlhHtGhJEm_VzzohzLXJaAIylB-Eo";
    JwtParser parser = Jwts.parser();
    Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
    Claims claims = claimsJws.getBody();
    System.out.println(claims.get("username"));
    System.out.println(claims.get("role"));
    System.out.println(claims.getId());
    System.out.println(claims.getSubject());
    System.out.println(claims.getExpiration());
}

运行结果如下

image-20211011131814795

springboot中使用jwt

  1. 添加上述依赖

  2. 创建用户Model

    image-20211011134808605

    package zjc.jwt_001.entity;
    
    import lombok.Data;
    
    @Data
    public class User {
    	private String username;
    	private String password;
    	private String token;
    }
    
    
  3. 创建jwt工具类

    image-20211011134902291

    package zjc.jwt_001.util;
    
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import java.util.Date;
    import java.util.UUID;
    
    public class JwtUtil {
        private static long time = 1000 * 60 * 60 * 24;
        private static String signature = "dasdia";
    
        public static String createToken() {
            JwtBuilder jwtBuilder = Jwts.builder(); // 构建jwt对象
            return jwtBuilder
                // header
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                // payload
                .claim("username", "张三")
                .claim("role", "admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis() + time)) // 有效期
                .setId(UUID.randomUUID().toString())
                // signature
                .signWith(SignatureAlgorithm.HS256, signature)
                .compact();
        }
    }
    
    
  4. 添加相应的controller

    image-20211011134818006

    package zjc.jwt_001.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import zjc.jwt_001.entity.User;
    import zjc.jwt_001.util.JwtUtil;
    
    @RestController
    public class UserController {
    
        private final String USERNAME = "admin";
        private final String PASSWORD = "123456";
    
        @GetMapping("/login")
        public User login(User user) {
            if (USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())) {
                user.setToken(JwtUtil.createToken());
                return user; // 验证成功
            }
            return null;
        }
    }
    
    
  5. 跨域设置

    package zjc.jwt_001.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class CorsConfiguration implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
        }
    }
    
    
  6. 测试访问

    访问localhost:8080/login

    其中用户名和密码设定如下

    image-20211011135046717

    访问页面

    image-20211011135105749

    获取如下数据

    {
        "username": "admin",
        "password": "123456",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTc2MDIsImp0aSI6IjQwMzk2ZWI0LTRkOTEtNDgwYS05YzExLWNiNGUzNzJjZmE5YiJ9.HqMgqiT6CQkZNNBm44KwQsJA8IFa0B6yvBC5wx53jM8"
    }
    

springboot + vue

实现功能:

前端登录并且登陆成功之后储存登录信息

创建一个登陆页面

<template>
	<div>
		<el-card class="box-card">
			<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
				<el-form-item label="用户名" prop="username">
					<el-input v-model="ruleForm.username" autocomplete="off"></el-input>
				</el-form-item>
				<el-form-item label="密码" prop="password">
					<el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
				</el-form-item>
				<el-form-item>
					<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
					<el-button @click="resetForm('ruleForm')">重置</el-button>
				</el-form-item>
			</el-form>
		</el-card>
	</div>
</template>

<script>
import axios from 'axios'
export default {
	name: 'Login',
	data() {
		var validateUsername = (rule, value, callback) => {
			if (value === '') {
				callback(new Error('请输入用户名'));
			} else {
				return callback()
			}
		};
		var validatePass = (rule, value, callback) => {
			if (value === '') {
				callback(new Error('请输入密码'));
			} else {
				return callback()
			}
		};
		return {
			ruleForm: {
				username: '',
				password: '',
			},
			rules: {
				password: [
					{ validator: validatePass, trigger: 'blur' }
				],
				username: [
					{ validator: validateUsername, trigger: 'blur' }
				],
			}
		};
	},
	methods: {
		submitForm(formName) {
			this.$refs[formName].validate((valid) => {
				if (valid) {
					let _this = this
					axios.get("http://localhost:8080/login", {params: _this.ruleForm}).then(function(resp) {
						if (resp.data != null) {
							// 如果登陆成功,将jwt储存起来
							localStorage.setItem('access-admin', JSON.stringify(resp.data))
							_this.$router.replace({path: '/'})
						}
					})
				} else {
					console.log('error submit!!');
					return false;
				}
			});
		},
		resetForm(formName) {
			this.$refs[formName].resetFields();
		}
	}
}
</script>

<style>
  .text {
    font-size: 14px;
  }

  .item {
    padding: 18px 0;
  }

  .box-card {
    width: 480px;
  }
</style>

token校验

因为token有有效期,所以需要在页面跳转时对token进行是否失效的校验

后端编写校验工具

在JwtUtil类中添加静态方法

public static boolean checkToken(String token) {
    if (token == null) { // token不存在
        return false;
    }
    try {
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
    } catch (Exception e) {
        return false;
    }
    return true; // 看是否能解码token,不能解码说明token无效
}

创建校验token的controller

@GetMapping("/checkToken")
public boolean checkToken(HttpServletRequest request) {
    String token = request.getHeader("token");
    return JwtUtil.checkToken(token);
}

前端在router增加过滤

image-20211011161855922

增加以下函数

router.beforeEach((to, from, next) => {
  if (to.path.startsWith('/login')) {
    window.localStorage.removeItem('access-admin') // 如果去登陆页面那就清除本地登录信息
    next()
  } else {
      // 如果是去其他页面那就校验本地登录信息,如果校验结果为true那就继续,为false就跳转到/error页面
    let admin = JSON.parse(window.localStorage.getItem('access-admin'))
    if (!admin) {
      next({path: '/login'})
    } else {
      axios({
        url: 'http://localhost:8080/checkToken',
        method: 'get',
        headers: {
          token: admin.token
        }
      }).then(resp => {
        if (!resp.data) {
          console.log('校验失败')
          next({path: '/error'})
        }
      })
      next()
    }
  }
})

创建error页面

<template>
	
</template>

<script>
export default {
	name: 'Error',
	created() {
		this.$alert('登录信息失效', '提示', {
			confirmButtonText: '确定'
		}).then(resp => {
			localStorage.removeItem('access-admim')
			this.$router.replace({path: '/login'}) // 提示之后跳转到登陆页面
		})
	}
}
</script>

本文作者:七つ一旋桜

本文链接:https://www.cnblogs.com/poifa/p/15392889.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   七つ一旋桜  阅读(125)  评论(0编辑  收藏  举报
(评论功能已被禁用)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起