热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

怎么在SpringBoot中使用token实现一个登录校验功能

这期内容当中小编将会给大家带来有关怎么在SpringBoot中使用token实现一个登录校验功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这

这期内容当中小编将会给大家带来有关怎么在SpringBoot中使用token实现一个登录校验功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1、基于session登录

基于session的登录(有回话状态),用户携带账号密码发送请求向服务器,服务器进行判断,成功后将用户信息放入session,用户发送请求判断session中是否有用户信息,有的话放行,没有的话进行拦截,但是考虑到时App产品,牵扯到要判断用户的session,需要sessionID,还要根据sessionId来获取session,在进行校验,还有sessionId的一个存储等等,所以没考虑用session

2、基于token登录

基于token的登录,是不存在回话状态,大概思路,在用户初次等路的时候,校验用户账号密码,成功后给其生成一个token,token=用户ID+时间戳+过期时间+一个自己平台规定的签名,使用jjwt生成一个令牌,然后对其进行存库,用户每次访问接口,都会在头部Headers中带上token,后来拦截器对其进行拦截,如果token为空或错误则让其登录,如果有token,获取token进行其解析,取出里面的用户ID,根据用户ID查询数据库中所存token,判断其是否正确,正确使其登录,错误则提示登录,大致思路就是这样,下面开始代码

导入jar包


 io.jsonwebtoken
 jjwt
 0.9.0

开发步骤

1、创建token库

怎么在SpringBoot中使用token实现一个登录校验功能

2、创建token实体类

package com.prereadweb.user.entity;
 
import lombok.Data;
 
/**
 * @Description: Token实体类
 * @author: Yangxf
 * @date: 2019/4/14 12:53
 */
@Data
public class TokenEntity {
 
 /* tokenId */
 private Long id;
 
 /* 用户ID */
 private Long userId;
 
 /* 刷新时间 */
 private int buildTime;
 
 /* token */
 private String token;
 
}

3、编写token的三个方法(添加、查询、修改)

package com.prereadweb.user.mapper;
 
import com.prereadweb.user.entity.TokenEntity;
import org.apache.ibatis.annotations.Mapper;
 
/**
 * @Description: Token数据库持久层接口
 * @author: Yangxf
 * @date: 2019/4/14 13:00
 */
@Mapper
public interface TokenMapper {
 
 /* 添加token */
 void addToken(TokenEntity token);
 
 /* 修改token */
 void updataToken(TokenEntity token);
 
 /* 查询token */
 TokenEntity findByUserId(Long userId);
 
}

4、创建拦截器

package com.prereadweb.user.interceptor;
 
import com.prereadweb.user.entity.TokenEntity;
import com.prereadweb.user.mapper.TokenMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
 
/**
 * @Description:拦截器
 * @author: Yangxf
 * @date: 2019/4/14 12:58
 */
public class LoginInterceptor implements HandlerInterceptor {
 
 @Autowired
 protected TokenMapper tokenMapper;
 //提供查询
 @Override
 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
  throws Exception {}
 @Override
 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
  throws Exception {}
 @Override
 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
 //此处为不需要登录的接口放行
 if (arg0.getRequestURI().contains("/login") || arg0.getRequestURI().contains("/register") || arg0.getRequestURI().contains("/error") || arg0.getRequestURI().contains("/static")) {
  return true;
 }
 //权限路径拦截
 //PrintWriter resultWriter = arg1.getOutputStream();
 // TODO: 有时候用PrintWriter 回报 getWriter() has already been called for this response
 //换成ServletOutputStream就OK了
 arg1.setContentType("text/html;charset=utf-8");
 ServletOutputStream resultWriter = arg1.getOutputStream();
 final String headerToken=arg0.getHeader("token");
 //判断请求信息
 if(null==headerToken||headerToken.trim().equals("")){
  resultWriter.write("你没有token,需要登录".getBytes());
  resultWriter.flush();
  resultWriter.close();
  return false;
 }
 //解析Token信息
 try {
  Claims claims = Jwts.parser().setSigningKey("preRead").parseClaimsJws(headerToken).getBody();
  String tokenUserId=(String)claims.get("userId");
  long iTokenUserId = Long.parseLong(tokenUserId);
  //根据客户Token查找数据库Token
  TokenEntity myToken= tokenMapper.findByUserId(iTokenUserId);
 
  //数据库没有Token记录
  if(null==myToken) {
  resultWriter.write("我没有你的token?,需要登录".getBytes());
  resultWriter.flush();
  resultWriter.close();
  return false;
  }
  //数据库Token与客户Token比较
  if( !headerToken.equals(myToken.getToken()) ){
  resultWriter.print("你的token修改过?,需要登录");
  resultWriter.flush();
  resultWriter.close();
  return false;
  }
  //判断Token过期
  Date tokenDate= claims.getExpiration();
  int overTime=(int)(new Date().getTime()-tokenDate.getTime())/1000;
  if(overTime>60*60*24*3){
  resultWriter.write("你的token过期了?,需要登录".getBytes());
  resultWriter.flush();
  resultWriter.close();
  return false;
  }
 
 } catch (Exception e) {
  resultWriter.write("反正token不对,需要登录".getBytes());
  resultWriter.flush();
  resultWriter.close();
  return false;
 }
 //最后才放行
 return true;
 }
 
}

5、配置拦截器

package com.prereadweb.user.config;
 
import com.prereadweb.user.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
/**
 * @Description: 拦截器配置
 * @author: Yangxf
 * @date: 2019/4/14 13:09
 */
@Configuration
public class LoginConfiguration implements WebMvcConfigurer {
 
 /**
 * @Function: 这个方法才能在拦截器中自动注入查询数据库的对象
 * @author: YangXueFeng
 * @Date: 2019/4/14 13:10
 */
 @Bean
 LoginInterceptor loginInterceptor() {
 return new LoginInterceptor();
 }
 
 /**
 * @Function: 配置生成器:添加一个拦截器,拦截路径为login以后的路径
 * @author: YangXueFeng
 * @Date: 2019/4/14 13:10
 */
 @Override
 public void addInterceptors(InterceptorRegistry registry ){
 registry.addInterceptor(loginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/static");
 }
}

6、登录

controller层

 @RequestMapping("/getlogin")
 public Object login(@Param("..") LoginQueryForm loginForm) {
 return userViewService.login(loginForm);
 }

serrvice层

@Override
 public Map login(LoginQueryForm loginForm) {
 
 Map map = new HashMap<>();
 //手机验证码登录
 if(!Util.isEmpty(loginForm.getPhoneCode())) {
  return phoneCodeLogin(loginForm, map);
 }
 //判断用户信息为空
 if (Util.isEmpty(loginForm.getPhone()) || Util.isEmpty(loginForm.getLoginPwd())) {
  return checkParameter(map);
 }
 //根据手机号查询user对象
 UserEntity user = userMapper.getUser(loginForm.getPhone());
 
 //判断用户不存在
 if (Util.isEmpty(user)) {
  map.put("code", UserStatusEnum.USER_NON_EXISTENT.intKey());
  map.put("msg", UserStatusEnum.USER_NON_EXISTENT.value());
  return map;
 }
 /* 判断密码 */
 if(!MD5Util.string2MD5(loginForm.getLoginPwd()).equals(user.getLoginPwd())){
  map.put("code", UserStatusEnum.PWD_ERROR.intKey());
  map.put("msg", UserStatusEnum.PWD_ERROR.value());
  return map;
 }
 
 //根据数据库的用户信息查询Token
 return operateToKen(map, user, user.getId());
 }

token操作

private Map operateToKen(Map map, UserEntity user, long userId) {
 //根据数据库的用户信息查询Token
 TokenEntity token = tokenmapper.findByUserId(userId);
 //为生成Token准备
 String TokenStr = "";
 Date date = new Date();
 int nowTime = (int) (date.getTime() / 1000);
 //生成Token
 TokenStr = creatToken(userId, date);
 if (null == token) {
  //第一次登陆
  token = new TokenEntity();
  token.setToken(TokenStr);
  token.setBuildTime(nowTime);
  token.setUserId(userId);
  token.setId(Long.valueOf(IdUtils.getPrimaryKey()));
  tokenmapper.addToken(token);
 }else{
  //登陆就更新Token信息
  TokenStr = creatToken(userId, date);
  token.setToken(TokenStr);
  token.setBuildTime(nowTime);
  tokenmapper.updataToken(token);
 }
 UserQueryForm queryForm = getUserInfo(user, TokenStr);
 /* 将用户信息存入session */
 /*SessionContext sessionContext = SessionContext.getInstance();
 HttpSession session = sessionContext.getSession();
 httpSession.setAttribute("userInfo", user);*/
 //返回Token信息给客户端
 successful(map);
 map.put("data", queryForm);
 return map;
 }

生成token

private String creatToken(Long userId, Date date) {
 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
 JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT") // 设置header
  .setHeaderParam("alg", "HS256").setIssuedAt(date) // 设置签发时间
  .setExpiration(new Date(date.getTime() + 1000 * 60 * 60))
  .claim("userId",String.valueOf(userId) ) // 设置内容
  .setIssuer("lws")// 设置签发人
  .signWith(signatureAlgorithm, "签名"); // 签名,需要算法和key
 String jwt = builder.compact();
 return jwt;
 }

上述就是小编为大家分享的怎么在SpringBoot中使用token实现一个登录校验功能了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程笔记行业资讯频道。


推荐阅读
author-avatar
欣欣大妮
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有