基于spring-boot的token登录验证
1.什么是token
用百度百科的话说就是密令的意思,通俗的来讲就是打开密码锁的钥匙.它其实就是一串很长的字符串(由3段字符串组成,具体哪3段后面说).
2.token是干什么的
token的出现是为了解决用户在登录时需要频繁去数据库读取用户信息来作验证而给数据库带来的巨大压力.相信大家看到这里比较懵,究竟他是怎么解决的呢!上面说了token由3部分组成,头部(header),载荷(playload)和签证(signature).在用户初次登录时,前端设置token默认为空,这时候根据用户的姓名,密码,过期时间等通过加密生成token存入数据库,并返回给前端,以后每次发起请求时request都会带着返回的token访问服务器,服务器解码后判断该token是否过期等相应的操作,这样一来就减少了数据库的访问压力.(如果还有懵逼的朋友,请谅解我的表达能力,继续往下看!!!)
3.token的组成
上面我们说了token由3部分组成.下面我们看看是哪3部分
(1).头部(header)
这部分组成很简单
typ:生成的密钥(token)的类型,我们这里用jwt(json web token)
alg:生成密钥的加密算法,我们这用HS256
例如:
{
“typ” : “JWT”,
“alg” : “HS256”
}
(2).载荷(playload)
这部分就比较复杂了,用来存放有效信息
一般由3部分组成
1.标准注册的说明
2.公共的说明
3.私有的声明
标准注册的声明(需要什么就用什么)
iss:jwt签发者
sub:jwt所面向的用户
aud:接受jwt的一方
exp:jwt的过期时间必须要大于签发时间
nbf:定义在什么时间之前,该jwt是不可用的
jti:jwt的唯一身份标识,主要用来作为一次性token
列如
{
“name”:“张三”,
“age”:“23”,
“company”:“小米”
}
(3)签证消息(signature)
这部分由3小部分构成
header(加密后的)
playload(加密后的)
secret(密钥)
好了token的组成也大概介绍完了,下面就介绍如何搭建demo了.
基于boot的token登录
第一步:先引入jwt的依赖
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
第二步:配置我们需要的参数
server:
port: 8088 //这是Tomcat的端口号
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver //这里是数据库驱动
url: //这是数据库的url,注意mysql5.0版本要加时区(serverTimezone=CTT),否则报错
username: //数据库名称
password: //该数据库密码
mybatis:
mapper-locations: //mybatis的mapper路径 classpath:
type-aliases-package: //要映射的包的别名
configuration:
map-underscore-to-camel-case: false
第三步:编写我们的mapper映射文件
<?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.hongsi.tokenlogin.dao.UserDao">
<insert id="insert" parameterType="user">
insert into user(name,passwd,username,token) values(#{name},#{passwd},#{username},#{token})
</insert>
<select id="select" parameterType="user" resultType="user">
select * from user where name = #{name}
</select>
<update id="update" parameterType="user">
update user set token=#{token} where id = #{id}
</update>
</mapper>
第四步:编写我们的实体类
import lombok.Data;
@Data
public class User {
private String name;
private String username;
private String passwd;
private String token;
private Integer id;
}
//@Data是由lombok提供的方法,可以自动生成set,get,toString等方法
第五步:编写我们的mybatis的dao接口
@Mapper
public interface UserDao {
public void insert(User user);
public User select(User user);
public void update(User user);
}
//注意一定要加@Mapper注解,否则mapper文件找不到改接口
第六步:编写我们的工具类(用来生成token和解密token的)
@Component
public class JwtToken {
//设置一个密钥,用来服务端验证token
// public final String secret = "ilovechina";
public String createToken(User user,String secret){
// String secret = "ilovechina";
//iatDate:签发时间
Date iatDate = new Date();
//expDate:过期时间
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE,3);
Date expDate = nowTime.getTime();
Map<String,Object> map = new HashMap<String,Object>();
map.put("typ","JWT");
map.put("alg","HS256");
//生成token
String token = JWT.create().withHeader(map)
.withClaim("name",user.getName())
.withClaim("passwd",user.getPasswd())
.withClaim("username",user.getPasswd())
.withExpiresAt(expDate)
.withIssuedAt(iatDate)
.sign(Algorithm.HMAC256(secret));
System.out.println(token);
return token;
}
//token解密
public Map<String,Claim> verifyToken(String token,String secret){
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
DecodedJWT decodedJWT = verifier.verify(token);
return decodedJWT.getClaims();
}
}
//注意要加@Component注解,将该工具类注入到beanFactory中
第七步:开始我们的核心controller的编写
建议大家有时间的还是按mvc的模式来写这个demo,养成良好的编码习惯,小编比较忙就直接上controller了
@RestController
@RequestMapping("/user")
public class LoginController {
@Resource
private UserDao userDao;
@Resource
private JwtToken jwtToken;
@RequestMapping("/login")
public String login(@RequestBody User user, HttpServletRequest request){
String secret = "ilovechina";//自定义密玥
String token = request.getHeader("token");//获取请求头中携带的token
//对获取的token进行判断,是否为空,为空则重新登录
if (token == null || token.length() ==0 || token.equals("null")) {
token = jwtToken.createToken(user,secret);//传入用户信息和密钥进行加密
user.setToken(token);
userDao.insert(user);//存入数据库
return "登录成功!"+token;
}
Map<String,Claim> map = jwtToken.verifyToken(token,secret);//如果获取到的token不为空,则进行解密,加以判断
String name = map.get("name").asString();
user.setName(name);
user = userDao.select(user);//根据token中用户名查找对应的密码,判断密码是否更改,更改则同步刷新token
if(!user.getPasswd().equals(map.get("passwd").asString())){
user.setToken(jwtToken.createToken(user,secret));
userDao.update(user);
return "您的密码已更改,系统已自动为您更新token!"+jwtToken.createToken(user,secret);
}
return "登录成功,无需重复登录!";
}
}
建议把这些业务写在拦截器中,用拦截器拦截请求并验证token
下面贴出我的postman测试
当我们第一次登录时,token为我们设置的默认值null
此时登录成功,并返回token
这时我们用刚刚返回的token去测试
看到我们需要的效果了
此时当我们修改数据库中的密码时
再用刚刚生成的token去验证时,你会发现已经成功更新了token(数据库中也已更新,这里就不贴出来了)并返回给了前端,方便下一次的认证