登录模式
基于session登录
基于session的登录(有回话状态),用户携带账号密码发送请求向服务器,服务器进行判断,成功后将用户信息放入session,用户发送请求判断session中是否有用户信息,有的话放行,没有的话进行拦截,但是考虑到时App产品,牵扯到要判断用户的session,需要sessionID,还要根据sessionId来获取session,在进行校验,还有sessionId的一个存储等等
基于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.1</version>
</dependency>
创建token库
创建token实体类
package com.model.entity;
import lombok.Data;
/**
* @Description: Token实体类
* @author: z
* @date: 2023/4/14 12:53
*/
@Data
public class TokenEntity {
/* tokenId */
private Long id;
/* 用户ID */
private Long userId;
/* 刷新时间 */
private int buildTime;
/* token */
private String token;
}
编写token的三个方法(添加、查询、修改)
package com.model.mapper;
import com.mode.entity.TokenEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* @Description: Token数据库持久层接口
* @author: z
* @date: 2023/4/14 12:53
*/
@Mapper
public interface TokenMapper {
/* 添加token */
void addToken(TokenEntity token);
/* 修改token */
void updataToken(TokenEntity token);
/* 查询token */
TokenEntity findByUserId(Long userId);
}
创建拦截器
package com.config.interceptor;
import com.model.entity.TokenEntity;
import com.model.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: z
* @date: 2023/4/14 12:53
*/
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;
}
}
配置拦截器
package com.config;
import com.config.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: z
* @date: 2023/4/14 12:53
*/
@Configuration
public class LoginConfiguration implements WebMvcConfigurer {
/**
* @Function: 这个方法才能在拦截器中自动注入查询数据库的对象
* @author: z
* @Date: 2023/4/14 12:53
*/
@Bean
LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
/**
* @Function: 配置生成器:添加一个拦截器,拦截路径为login以后的路径
* @author: z
* @Date: 2023/4/14 12:53
*/
@Override
public void addInterceptors(InterceptorRegistry registry ){
registry.addInterceptor(loginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/static");
}
}
登录
controller层
@PostMapping("/getlogin")
public Result login(@RequestBody LoginQueryForm loginForm) {
return Result.ok(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
原文章地址:
http://www.3qphp.com/java/framework/3303.html