代码地址如下:
http://www.demodashi.com/demo/12531.html

0、准备工作

0-1运行环境

  1. jdk1.8
  2. maven
  3. 一个能支持以上两者的代码编辑器,作者使用的是IDEA。

0-2知识储备

  1. 对SpringBoot框架有所了解
  2. 对token的定义有了解
  3. 本案例简单,代码注释较少,有不明白的地方,或者不正确的地方,欢迎联系作者本人或留言。

1、设计思路

1-1 项目结构

本案例模拟用户登录/注册后,服务器返回一个token用于用户后续操作。

/controller/HelloController.java:测试token的接口

/controller/UserController.java:用户注册/登录的接口

/entity/User.java:用户实体类,属性有 用户ID,用户名,密码,邮箱,上次登录时间。

/repository/UserRepository:实现SpringData接口的数据库操作类,可以很方便的进行CRUD等操作。

/Reponse/UserResponse.java:接口返回数据的实体类。

/util/Constants.java:存放常量的工具类。

/util/JwtUtil.java:Jwt工具类,可以生成、解析Jwt。

1-2 实现难点

  1. 对Jwt的理解,可以参考http://blog.leapoahead.com/2015/09/06/understanding-jwt/
  2. JwtUtil工具类的开发

2 具体实现

2-1 JwtUtil.java

@Component
public class JwtUtil { private static UserRepository userRepository; @Autowired
public JwtUtil(UserRepository userRepository) {
JwtUtil.userRepository = userRepository;
} public static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hour
static final String SECRET = "ThisIsASecret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization"; public static String generateToken(String username,Date generateTime) {
HashMap<String, Object> map = new HashMap<>();
//可以把任何安全的数据放到map里面
map.put("username", username);
map.put("generateTime",generateTime);
String jwt = Jwts.builder()
.setClaims(map)
.setExpiration(new Date(generateTime.getTime() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
return jwt;
} /**
* @param token
* @return
*/
public static Map<String,Object> validateToken(String token) {
Map<String,Object> resp = new HashMap<String,Object>();
if (token != null) {
// 解析token
try {
Map<String, Object> body = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
String username = (String) (body.get("username"));
Date generateTime = new Date((Long)body.get("generateTime")); if(username == null || username.isEmpty()){
resp.put("ERR_MSG",Constants.ERR_MSG_USERNAME_EMPTY);
return resp;
}
//账号在别处登录
if(userRepository.findByUsername(username).getLastLoginTime().after(generateTime)){
resp.put("ERR_MSG",Constants.ERR_MSG_LOGIN_DOU);
return resp;
}
resp.put("username",username);
resp.put("generateTime",generateTime);
return resp;
}catch (SignatureException | MalformedJwtException e) {
// TODO: handle exception
// don't trust the JWT!
// jwt 解析错误
resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_ERR);
return resp;
} catch (ExpiredJwtException e) {
// TODO: handle exception
// jwt 已经过期,在设置jwt的时候如果设置了过期时间,这里会自动判断jwt是否已经过期,如果过期则会抛出这个异常,我们可以抓住这个异常并作相关处理。
resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_EXP);
return resp;
}
}else {
resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_EMPTY);
return resp;
}
}
}

2.2 UserController.java

@RestController
@RequestMapping("/user")
public class UserController { @Autowired
private UserRepository userRepository; //注册或登录
@RequestMapping("/login")
@Transactional
public UserResponse login(User user){ String username = user.getUsername();
String password = user.getPassword();
//TODO 检验参数的完整性 UserResponse userResponse = new UserResponse();
User tUser = userRepository.findByUsername(username);
//检验username是否存在
user.setLastLoginTime(new Date());
if(tUser!=null){
//检验密码是否正确
if(!tUser.getPassword().equals(password)) {
userResponse.setErrorNum(Constants.ERR_NUM_PWD_ERR);
userResponse.setErrorMsg(Constants.ERR_MSG_PWD_ERR);
return userResponse;
}
userRepository.updateLastLoginTimeByUserName(user.getLastLoginTime(),username); }else {
try {
tUser = userRepository.save(user);
} catch (Exception e) {
userResponse.setErrorNum(Constants.ERR_NUM_SERVER_ERR);
userResponse.setErrorMsg(Constants.ERR_MSG_SERVER_ERR);
return userResponse;
}
}
userResponse.setErrorNum(Constants.ERR_NUM_OK);
userResponse.setErrorMsg(Constants.ERR_MSG_OK);
userResponse.setUserName(username);
userResponse.setUserId(tUser.getId());
userResponse.setToken(JwtUtil.generateToken(username,user.getLastLoginTime())); return userResponse;
}
}

2.3 HelloController.java

@RestController
public class HelloController {
@RequestMapping("/hello")
public Map login(HttpServletRequest request){
String token = request.getParameter("token");
return JwtUtil.validateToken(token);
}
}

2.4测试

用户登录后,会返回一串token,咱们可以用token进行下一步请求:

当我再次访问登录接口之后,却用旧的token访问测试接口时:

3 总结

上面是贴出的主要代码,完整的请下载demo包,有不明白的地方请在下方评论,或者联系邮箱yaoyunxiaoli@163.com。

我是妖云小离,这是我第二次在Demo大师上发文章,感谢阅读。

基于JWT的Token开发案例

代码地址如下:
http://www.demodashi.com/demo/12531.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

基于JWT的Token开发案例的更多相关文章

  1. ASP.NET WebApi 基于JWT实现Token签名认证

    一.前言 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NET WebServi ...

  2. token 与 基于JWT的Token认证

    支持跨域访问,无状态认证 token特点 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输 无状态(也称:服务端可扩展行): ...

  3. 基于JWT的Token认证机制及安全问题

    [干货分享]基于JWT的Token认证机制及安全问题 https://bbs.huaweicloud.com/blogs/06607ea7b53211e7b8317ca23e93a891

  4. ASP.NET Web API 2系列(四):基于JWT的token身份认证方案

    1.引言 通过前边的系列教程,我们可以掌握WebAPI的初步运用,但是此时的API接口任何人都可以访问,这显然不是我们想要的,这时就需要控制对它的访问,也就是WebAPI的权限验证.验证方式非常多,本 ...

  5. 基于JWT的Token身份验证

    ​ 身份验证,是指通过一定的手段,完成对用户身份的确认.为了及时的识别发送请求的用户身份,我们调研了常见的几种认证方式,cookie.session和token. 1.Cookie ​ cookie是 ...

  6. 基于JWT实现token验证

    JWT的介绍 Json Web Token(JWT)是目前比较流行的跨域认证解决方案,是一种基于JSON的开发标准,由于数据是可以经过签名加密的,比较安全可靠,一般用于前端和服务器之间传递信息,也可以 ...

  7. iOS 开发之基于JWT的Token认证机制及解析

    在移动端和服务端通信中,一般有两种认证方式:token 和 session. 1.session/cookie 认证机制: 在服务端创建一个Session对象,同时在客户端的浏览器端创建一个Cooki ...

  8. 基于jwt的token验证

    一.什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). 该token被设计为紧凑且安全的,特别适用于分布 ...

  9. 基于JWT的token身份认证方案

    一.使用JSON Web Token的好处? 1.性能问题. JWT方式将用户状态分散到了客户端中,相比于session,可以明显减轻服务端的内存压力. Session方式存储用户id的最大弊病在于S ...

随机推荐

  1. 你不一定知道的、并没有什么卵用的一些python库

    1. delorean,用来处理时间的库 import datetime import pytz # 一般情况下,我们想表示时间的话 est = pytz.timezone("Asia/Sh ...

  2. 第一章:1-20、试计算以下两种情况的发送时延和传播时延: (1) 数据长度为107bit,数据发送速率为100kbit/s,传播距离为1000km,信号在媒体上 的传播速率为2×108m/s。 (2) 数据长度为103bit,数据发送速率为1Gbit/s,传输距离和信号在媒体上的传播速率同 上。

    <计算机网络>谢希仁著第四版课后习题答案答: 1):发送延迟=107/(100×1000)=100s         传播延迟=1000×1000/(2×108)=5×10-3s=5ms ...

  3. 将ros中suscriber和publisher写入class中

    相比于笨拙的全局变量和全局函数,将suscriber和publisher成一个class,形式更加简洁和容易管理,一个节点就是一个类 参考资料 http://answers.ros.org/quest ...

  4. django自定义signal的发送和接收样例

    想在项目中用上,就实习一下. # coding:utf8 from django.dispatch import Signal from django.dispatch import receiver ...

  5. (1)OracleClient数据库操作(淘汰)

    一.数据库连接 Oracle 数据提供程序,位于System.Data.OracleClient 命名空间.( .NET 4 以后的版本,会将不在维护和更新了) 第一步:引入命名空间 在程序的开头写上 ...

  6. Python的工具包[0] -> numpy科学计算 -> numpy 库及使用总结

    NumPy 目录 关于 numpy numpy 库 numpy 基本操作 numpy 复制操作 numpy 计算 numpy 常用函数 1 关于numpy / About numpy NumPy系统是 ...

  7. Codeforces 1018D D. Order book

    解法:用l,r分别代表buy的最大值和sell的最小值,add时,若添加的值在l,r之间,则该值有可能作为下一次accept的值 accept x时,x只能在区间[l,r]中,否则无解,若x为l或r, ...

  8. android利用adb修改手机的分辨率和dpi

    在android开发过程中,适配更多的适配是必不可少的一步,而每次测试适配时,要么购买设配,要么模拟器,买设配太花钱,模拟器太占内存,不过幸好还可以通过修改手机的size(分辨率)和density来进 ...

  9. MSSQL收缩事务日志&日志文件过大无法收缩

    原文:MSSQL收缩事务日志&日志文件过大无法收缩 一.MS SQL SERVER 2005 --1.清空日志 exec('DUMP TRANSACTION 数据库名 WITH NO_LOG' ...

  10. 查看linux内核版本及操作系统版本的命令

    一.查看内核版本命令: (1)[root@localhost ~]#cat/proc/version Linux version 2.6.18-238.el5 (mockbuild@x86-012.b ...