Java实现登录功能(一)
文章目录
- Java实现登录功能(一)
- 前言
- 一、Token的认证流程及优点
- 二、JWT
- 三、Java基于JWT实现登录功能
-
- 1.引入pom依赖
- 2.编写JWT工具类
- 3.编写登录接口
- 4.Java后端校验Token测试
- 5.Vue登录页面中的存储Token方法
- 6.Vue前端每次请求资源携带Token方法
- 总结
前言
刚开始学习Java的时候,你是否有过疑问,登录功能就是简单地查询数据库对比一下账号密码就行了吗?在进行权限认证的时候,又该怎么办呢?每次都在提交的表单上加上账号密码吗?在这种情况下又该怎么防止CSRF(跨站请求伪造)呢?
以下介绍一种现在流行的基于JWT的Token认证实现的登录功能。
一、Token的认证流程及优点
认证流程:
1.客户端发送带有用户名和密码参数的登录请求
2.服务端收到请求,验证用户名和密码
3.验证通过后,服务端签发Token并返回token给客户端
4.客户端收到Token后存储到Cookie或者Storage等中
5.客户端每次向服务端请求资源时需要在cookie或者header请求头中携带服务端签发的token
6.服务端收到请求后验证客户端请求携带的token,如果验证通过就向客户端返回请求数据
一般情况下,Token是被Header请求头所携带的。
有着以下优点:
1.支持跨域:Cookie是无法跨域的,而Token可以放在header请求头发送,所以支持跨域。
2.无状态:Token认证方式在服务端不需要像传统的Session认证方式那样去记录SessionId,因为Token本身包含了登录用户的信息,可以减轻服务端的压力。
3.支持移动端、小程序:当客户端是非浏览器平台时,Cookie是不被支持的,此时采用在Header请求头携带Token的认证方式会显得简单
4.能避免CSRF失效:由于CSRF依赖于Cookie,所以由Header请求头携带Token的认证方式不会发生CSRF,所以能避免CSRF
说明
CSRF——跨站请求伪造。其原理是通过伪造成其它内容的请求地址,诱导用户触发请求。如果用户在登录状态下触发请求,将会自动携带Cookie访问服务端,服务端接收请求后检查到Cookie内部的用户信息会认为是登录用户本人的操作。
二、JWT
JWT(JSON Web Token)是一种比较主流的使用规范。其结构分别由Header标头、Payload有效负载、Signature签名算法三部分组成。
1.Header负责记录令牌类型、签名算法等。
such as: {"alg":"HS256","type","JWT"}
2.Payload负责声明一些标准以及携带一些用户信息
{"userId":"1","username":"mayikt"}
标准声明:
iss:JWT签发者
sub:JWT所面向的用户
aud:JWT的接收者
exp:JWT的过期时间,过期时间必须大于签发时间
nbf:JWT的生效时间
iat:JWT的签发时间
jti:JWT的唯一身份标识,避免重复
自定义的声明:
一些不敏感的用户信息。
such as: {"iss":"CJJ","sub":"custom","exp":"1640000000","iat":"1632000000","jti":"156sad54a615a4d56ad15a6sd15s8924","userId":"1"}
3.Signature防止Token被篡改,加盐,确保安全性
三、Java基于JWT实现登录功能
1.引入pom依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.3</version>
</dependency>
2.编写JWT工具类
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.demo.domain.User;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTUtil {
//token有效时长
private static final long EXPIRE=30*60*1000L;
//token的密钥 可自行定义
private static final String SECRET="jwt";
public static String createToken(User user) throws UnsupportedEncodingException {
//token过期时间
Date date=new Date(System.currentTimeMillis()+EXPIRE);
//jwt的header部分
Map<String ,Object>map=new HashMap<>();
map.put("alg","HS256");
map.put("typ","JWT");
//使用jwt的api生成token
String token= JWT.create()
.withHeader(map)
.withClaim("username", user.getUsername())//私有声明
.withExpiresAt(date)//过期时间
.withIssuedAt(new Date())//签发时间
.sign(Algorithm.HMAC256(SECRET));//签名
return token;
}
/**
* 校验token的有效性
* 1 token的header和payload是否没改过
* 2 没有过期
*/
public static boolean verify(String token){
try {
//解密
JWTVerifier verifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
verifier.verify(token);
return true;
}catch (Exception e){
return false;
}
}
//无需解密也可以获取token的信息
public static String getUsername(String token){
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
}
3.编写登录接口
domain、mapper、service层请自行编写,其中Result类是统一返回结果集类
import com.example.demo.domain.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.JWTUtil;
import com.example.demo.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
@RestController
public class LoginController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestParam String username, @RequestParam String password) throws UnsupportedEncodingException {
User user=userService.getUserByPass(username, password);
if(user == null){
return Result.fail("用户名或密码错误");
}
//登录成功则调用JWTUtil类的创建Token方法返回客户端
String token= JWTUtil.createToken(user);
return Result.succ(200,"登陆成功",token);
}
}
4.Java后端校验Token测试
import com.example.demo.utils.Result;
import com.example.demo.utils.JWTUtil;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/test")
public Result test(HttpServletRequest request){
String token=req.getHeader("Authorization");
if(token == null){
return Result.fail("尚未登录");
}
//获取到token中的用户信息
System.out.println(JWTUtil.getUsername(token));
//可自行编写获取用户信息后的操作
//......
return Result.succ("test");
}
}
此处仅进行测试,后续博客将介绍安全框架搭配使用。
5.Vue登录页面中的存储Token方法
methods: {
submitLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true
this.postRequest('/login', this.loginForm, 'form').then((resp) => {
if (resp) {
// 存储用户token
const tokenStr = resp.obj.tokenHead + resp.obj.token
window.sessionStorage.setItem('tokenStr', tokenStr)
let path = this.$route.query.redirect
this.$router.replace(
path == '/' || path == undefined ? '/home' : path
)
}
})
} else {
this.$message.error('你还有未填写的信息')
return false
}
})
},
},
6.Vue前端每次请求资源携带Token方法
import axios from 'axios'
// 请求拦截器
axios.interceptors.request.use(config => {
if (window.sessionStorage.getItem('tokenStr')) {
//请求携带自定义token
config.headers['Authorization'] =
window.sessionStorage.getItem('tokenStr');
}
return config
}, error => {
console.log(error);
})
温馨提示:
遇到报错跨域问题可参考 解决跨域问题
总结
本文简单地介绍了基于JWT的Token认证方式实现的登录功能。