接口url:/register
请求方式:POST
请求参数:
| 参数名称 | 参数类型 | 说明 |
|---|---|---|
| account | string | 账号 |
| password | string | 密码 |
| nickname | string | 昵称 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": "token"
}
后端逻辑:
1.判断前端传入的参数是否为空,为空则返回参数错误
2.前端传入的参数不为空,判断用户是否以及存在,存在 返回以及被注册
3.用户不存在,注册用户,将对应userId传入JWT中创建token,并保存在redis里面
RegisterController:
package com.example.blog.controller;
import com.example.blog.service.LoginService;
import com.example.blog.vo.Result;
import com.example.blog.vo.params.LoginParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.PublicKey;
@RestController
@RequestMapping("/register")
public class RegisterController
{
@Autowired
private LoginService loginService;
@PostMapping
public Result register(@RequestBody LoginParams loginParams)
{
return loginService.register(loginParams);
}
}
LoginServiceImpl:
package com.example.blog.service.impl;
import com.alibaba.fastjson.JSON;
import com.example.blog.entity.SysUser;
import com.example.blog.service.LoginService;
import com.example.blog.service.SysUserService;
import com.example.blog.utils.JWTUtils;
import com.example.blog.vo.ErrorCode;
import com.example.blog.vo.Result;
import com.example.blog.vo.params.LoginParams;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
@Transactional
public class LoginServiceImpl implements LoginService
{
//加密盐
private static final String salt = "mszlu!@#";
@Lazy
@Autowired
private SysUserService sysUserService;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Result register(LoginParams loginParams)
{
String account = loginParams.getAccount();
String password = loginParams.getPassword();
String nickname = loginParams.getNickname();
if(StringUtils.isEmpty(account) || StringUtils.isEmpty(password) || StringUtils.isEmpty(nickname))
{
return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
}
SysUser sysUser = sysUserService.findUserByAccount(account);
if(sysUser != null)
{
return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(),ErrorCode.ACCOUNT_EXIST.getMsg());
}
sysUser = new SysUser();
sysUser.setNickname(nickname);
sysUser.setAccount(account);
password = DigestUtils.md5Hex(password+salt);
sysUser.setPassword(password);
sysUser.setCreateDate(System.currentTimeMillis());
sysUser.setLastLogin(System.currentTimeMillis());
sysUser.setAvatar("/static/suqi.jpg");
sysUser.setAdmin(1); //1 为true
sysUser.setDeleted(0); // 0 为false
sysUser.setSalt("");
sysUser.setStatus("");
sysUser.setEmail("");
sysUserService.save(sysUser);
String token = JWTUtils.createToken(sysUser.getId());
redisTemplate.opsForValue().set("TOKEN_"+token,JSON.toJSonString(sysUser),1,TimeUnit.DAYS);
return Result.success(token);
}
}
SysUserServiceImpl:
@Override
public SysUser findUserByAccount(String account)
{
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(SysUser::getAccount,account);
queryWrapper.last("limit 1");
return sysUserMapper.selectOne(queryWrapper);
}
public void save(SysUser sysUser)
{
sysUserMapper.insert(sysUser);
}
登录拦截器:
使用拦截器,进行登录拦截,如果遇到需要登录才能访问的接口,如果未登录,拦截器直接返回,并跳转登录页面。
package com.example.blog.handler;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.api.R;
import com.example.blog.entity.SysUser;
import com.example.blog.service.LoginService;
import com.example.blog.vo.ErrorCode;
import com.example.blog.vo.Result;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor
{
@Autowired
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
//执行方法(handler)之前执行该方法
if(!(handler instanceof HandlerMethod))
{
return true;
}
String token = request.getHeader("Authorization");
log.info("=================request start===========================");
String requestURI = request.getRequestURI();
log.info("request uri:{}",requestURI);
log.info("request method:{}",request.getMethod());
log.info("token:{}", token);
log.info("=================request end===========================");
if(StringUtils.isEmpty(token))
{
Result result = Result.fail(ErrorCode.TOKEN_ERROR.getCode(),ErrorCode.TOKEN_ERROR.getMsg());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSON.toJSonString(result));
return false;
}
SysUser sysUser = loginService.checkToken(token);
if(sysUser == null)
{
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSON.toJSonString(result));
return false;
}
return true;
}
}
最后,配置在MvcConfig里面
package com.example.blog.cofig;
import com.example.blog.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer
{
@Autowired
private LoginInterceptor loginInterceptor;
//跨域配置,不可设置为*,不安全, 前后端分离项目,可能域名不一致
//本地测试 端口不一致 也算跨域
@Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/test")
.excludePathPatterns("/login")
.excludePathPatterns("/register");
}
}
ThreadLocal保存用户信息
UserThreadLocal:
package com.example.blog.utils;
import com.example.blog.entity.SysUser;
public class UserThreadLocal
{
private UserThreadLocal(){}
//线程变量隔离
private static final ThreadLocal LOCAL = new ThreadLocal<>();
public static void put(SysUser sysUser)
{
LOCAL.set(sysUser);
}
public static SysUser get()
{
return LOCAL.get();
}
public static void remove()
{
LOCAL.remove();
}
}
package com.example.blog.handler;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.api.R;
import com.example.blog.entity.SysUser;
import com.example.blog.service.LoginService;
import com.example.blog.utils.UserThreadLocal;
import com.example.blog.vo.ErrorCode;
import com.example.blog.vo.Result;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor
{
@Autowired
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
//执行方法(handler)之前执行该方法
if(!(handler instanceof HandlerMethod))
{
return true;
}
String token = request.getHeader("Authorization");
log.info("=================request start===========================");
String requestURI = request.getRequestURI();
log.info("request uri:{}",requestURI);
log.info("request method:{}",request.getMethod());
log.info("token:{}", token);
log.info("=================request end===========================");
if(StringUtils.isEmpty(token))
{
Result result = Result.fail(ErrorCode.TOKEN_ERROR.getCode(),ErrorCode.TOKEN_ERROR.getMsg());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSON.toJSonString(result));
return false;
}
SysUser sysUser = loginService.checkToken(token);
if(sysUser == null)
{
Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg());
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSON.toJSonString(result));
return false;
}
UserThreadLocal.put(sysUser);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
{
//如果不删除 ThreadLocal中的用户信息 会有内存泄露的风险
UserThreadLocal.remove();
}
}
ThreadLocal内存泄露的原因:
实线代表强引用,虚线代表弱引用
每一个Thread维护一个ThreadLocalMap, key为使用弱引用的ThreadLocal实例,value为线程变量的副本。
强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。
如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。
弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。
从上图中可以看出,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收。
这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。
但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。



