SpringBoot框架集成token实现登录校验功能

简介

公司新项目,需要做移动端(Android和IOS),登录模块,两个移动端人员提出用token来校验登录状态,一脸懵懵的,没做过,对于token的基本定义都模棱两可,然后查资料查查查,最终OK完成,写篇博客记录一下

思路:

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包

<!-- 生成token -->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt</artifactId>
 <version>0.9.0</version>
</dependency>

开发步骤

1、创建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<String, Object> login(LoginQueryForm loginForm) {

  Map<String, Object> 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<String, Object> operateToKen(Map<String, Object> 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;
 }

至此,token登录OK

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue+springboot前后端分离实现单点登录跨域问题解决方法

    最近在做一个后台管理系统,前端是用时下火热的vue.js,后台是基于springboot的.因为后台系统没有登录功能,但是公司要求统一登录,登录认证统一使用.net项目组的认证系统.那就意味着做单点登录咯,至于不知道什么是单点登录的同学,建议去找一下万能的度娘. 刚接到这个需求的时候,老夫心里便不屑的认为:区区登录何足挂齿,但是,开发的过程狠狠的打了我一巴掌(火辣辣的一巴掌)...,所以这次必须得好好记录一下这次教训,以免以后再踩这样的坑. 我面临的第一个问题是跨域,浏览器控制台直接报CORS,

  • springboot实现拦截器之验证登录示例

    整理文档,搜刮出一个springboot实现拦截器之验证登录示例,稍微整理精简一下做下分享. 添加jar包,这个jar包不是必须的,只是在拦截器里用到了,如果不用的话,完全可以不引入 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dep

  • Springboot网站第三方登录 微信登录

    微信开放平台接入,官网:https://open.weixin.qq.com,在官网注册并添加应用后即可获得APP_ID和APP_SECRET. 步骤一:创建一个继承AuthService的接口,WeChatAuthService,如下 public interface WeChatAuthService extends AuthService { public JSONObject getUserInfo(String accessToken, String openId); } 步骤二:We

  • SpringBoot+SpringSecurity处理Ajax登录请求问题(推荐)

    最近在项目中遇到了这样一个问题:前后端分离,前端用Vue来做,所有的数据请求都使用vue-resource,没有使用表单,因此数据交互都是使用JSON,后台使用Spring Boot,权限验证使用了Spring Security,因为之前用Spring Security都是处理页面的,这次单纯处理Ajax请求,因此记录下遇到的一些问题.这里的解决方案不仅适用于Ajax请求,也可以解决移动端请求验证. 创建工程 首先我们需要创建一个Spring Boot工程,创建时需要引入Web.Spring S

  • SpringBoot创建JSP登录页面功能实例代码

    添加JSP配置 1.pom.xml添加jsp解析引擎 <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.s

  • SpringBoot + SpringSecurity 短信验证码登录功能实现

    实现原理 在之前的文章中,我们介绍了普通的帐号密码登录的方式: SpringBoot + Spring Security 基本使用及个性化登录配置. 但是现在还有一种常见的方式,就是直接通过手机短信验证码登录,这里就需要自己来做一些额外的工作了. 对SpringSecurity认证流程详解有一定了解的都知道,在帐号密码认证的过程中,涉及到了以下几个类:UsernamePasswordAuthenticationFilter(用于请求参数获取),UsernamePasswordAuthentica

  • SpringBoot+Shiro学习之密码加密和登录失败次数限制示例

    这个项目写到现在,基本的雏形出来了,在此感谢一直关注的童鞋,送你们一句最近刚学习的一句鸡汤:念念不忘,必有回响.再贴一张ui图片: 前篇思考问题解决 前篇我们只是完成了同一账户的登录人数限制shiro拦截器的编写,对于手动踢出用户的功能只是说了采用在session域中添加一个key为kickout的布尔值,由之前编写的KickoutSessionControlFilter拦截器来判断是否将用户踢出,还没有说怎么获取当前在线用户的列表的核心代码,下面贴出来: /** * <p> * 服务实现类

  • Springboot实现验证码登录

    本文实例为大家分享了Springboot实现验证码登录的具体代码,供大家参考,具体内容如下 因为在项目中需要使用到验证码,我总结一下在项目中如何快速解决项目需求~验证码,下面推荐给大家速上手验证码的例子. 一.编写验证码工具类 import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.FileOutputStr

  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    Spring Security 基本介绍 这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档 我就只说下SpringSecurity核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) 基本环境搭建 这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式 <dependencies> ... <dependency> <groupI

  • SpringBoot整合Shiro实现登录认证的方法

    安全无处不在,趁着放假读了一下 Shiro 文档,并记录一下 Shiro 整合 Spring Boot 在数据库中根据角色控制访问权限 简介 Apache Shiro是一个功能强大.灵活的,开源的安全框架.它可以干净利落地处理身份验证.授权.企业会话管理和加密. 上图是 Shiro 的基本架构 Authentication(认证) 有时被称为"登录",用来证明用户是用户他们自己本人 Authorization(授权) 访问控制的过程,即确定"谁"访问"什么

随机推荐