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());
}
运行结果如下
springboot中使用jwt
-
添加上述依赖
-
创建用户Model
package zjc.jwt_001.entity; import lombok.Data; @Data public class User { private String username; private String password; private String token; }
-
创建jwt工具类
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(); } }
-
添加相应的controller
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; } }
-
跨域设置
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("*"); } }
-
测试访问
访问
localhost:8080/login
其中用户名和密码设定如下
访问页面
获取如下数据
{ "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增加过滤
增加以下函数
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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步