SpringBoot学习项目-博客系统-part6

注册

  1. 根据文档可知请求地址、请求方式、请求参数

RegisterController

  • 为什么要用LoginService?
    因为注册了之后就形同于登陆,LoginService这个接口中定义个关于登陆、注册、退出、验证等。
@RestController
@RequestMapping("register")
public class RegisterController {

    @Autowired
    private LoginService loginService;

    @PostMapping
    public Result register(@RequestBody LoginParam loginParam){
        return loginService.register(loginParam);
    }
}
  1. 请求参数中有用户账户、用户密码、用户昵称
  • 因为前面已经有一个LoginParam类,这个类中有account、password属性,所以现在只需要根据现在的参数新增一个nickname属性
@Data
public class LoginParam {
    private String account;
    private String password;
    private String nickname;
}

LoginService

//注册
Result register(LoginParam loginParam);

LoginServiceImpl

  • 分析注册逻辑:
    1. 根据传入的参数得到账户、密码、昵称,如果这三个有一个为空,那么就返回一个传参错误,根据Errcode枚举类定义的。
    2. 都不为空,那么去UserService去根据账户找,因为账户名不能一样,如果找到了,说明被注册过,就返回一个账户已经存在。
    3. 没有对应的账户,那么就new一个用户,设置对应的属性,并且要保存到数据库中。
    4. 接下来就是和登陆逻辑一样了,创建token,保存在redis中,最后返回token(因为返回数据形式也是token)
@Override
    public Result register(LoginParam loginParam) {
         String account = loginParam.getAccount();
        String password = loginParam.getPassword();
        String nickname = loginParam.getNickname();
        if (StringUtils.isBlank(account)
                || StringUtils.isBlank(password)
                || StringUtils.isBlank(nickname)
        ){
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg());
        }
        SysUser sysUser = this.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);
        sysUser.setPassword(DigestUtils.md5Hex(password+slat));
        sysUser.setCreateDate(System.currentTimeMillis());
        sysUser.setLastLogin(System.currentTimeMillis());
        sysUser.setAvatar("/static/img/logo.b3a48c0.png");
        sysUser.setAdmin(1); //1 为true
        sysUser.setDeleted(0); // 0 为false
        sysUser.setSalt("");
        sysUser.setStatus("");
        sysUser.setEmail("");
        this.sysUserService.save(sysUser);

        //token
        String token = JWTUtils.createToken(sysUser.getId());

        redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
        return Result.success(token);
    }

SysUserService

 SysUser findUserByAccount(String account);

 void save(SysUser sysUser);

SysUserServiceImpl

  	@Override
    public SysUser findUserByAccount(String account) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getAccount,account);
        queryWrapper.last("limit 1");
        return sysUserMapper.selectOne(queryWrapper);
    }

    @Override
    public void save(SysUser sysUser) {
        //注意 默认生成的id 是分布式id 采用了雪花算法
        this.sysUserMapper.insert(sysUser);
    }

事务

  1. 本项目中没有涉及到钱什么的,为什么需要用到事务呢?
  • 假设如果服务端redis关闭了,那么意味着注册用户的时候不应该添加到数据库中,应该执行回滚操作。
  1. 加事务
  • 通常加以一般加在接口上
@Transactional
public interface LoginService {
    //登陆
    Result login(LoginParam loginParam);


//验证
    SysUser checkToken(String token);


//退出
    Result logout(String token);


//注册
    Result register(LoginParam loginParam);
}

登陆拦截

  1. 思考:为什幺登陆拦截?
  • 设想如果所有的接口访问都需要登陆,那么一旦登陆逻辑发生改变,变化的地方就过多,此时就需要登陆拦截,比如我只访问某些资源的时候才需要进行一个登陆。
  1. 使用拦截器进行登陆拦截,如果遇到需要登陆才能访问的接口,必须让其登陆,如果没有登陆,要跳转到登陆页面
  2. 怎样去配置这个拦截器?
  • 写一个LoginInterCeptor去实现HandlerInterceptor接口,去实现preHandle方法,每次之前都要进行一个拦截
  1. 拦截逻辑分析
  • 判断请求接口路径是否为判断请求的接口路径是否为HandlerMethod,访问的是controller的方法。handler可能是 RequestResourceHandler springboot程序访问静态资源默认去classpath下的static目录去查询
  • 接下来因为请求头的Authorization携带的token信息,去判断token是否为空,
    • 为空就没有登陆
    • 不为空,进行一个登陆验证,因为前面在登陆的时候已经写过该方法,所以要注入LoginService
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private LoginService loginService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        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 (token == null){
            Result result = Result.fail(ErrorCode.NO_LOGIN.getCode(), "未登录");
//            浏览器识别返回的是json
            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(), "未登录");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(JSON.toJSONString(result));
            return false;
        }
        //是登录状态,放行
        return true;
    }
}

ThreadLocal保存用户信息

  1. 思考:如何在登陆的时候获取到用户的信息呢?比如后续的写文章的操作。
  2. 考虑ThreadLocal(这个之后会专门写一个ThreadLocal的总结),现在只需要记住,ThreadLocal中的mapkey是ThreadLocal,value是要注入的值

自定义一个ThreadLocal

public class UserThreadLocal {

    private UserThreadLocal(){}
    //线程变量隔离
    private static final ThreadLocal<SysUser> 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();
    }
}

同时在每次拦截放行之后,说明是有用户存在,并且访问的是要登陆才能访问的资源,此时将查询出的用户放到UserThreadLocal中

UserThreadLocal.put(sysUser);

同时,在所有处理完成之后,要进行回收,不然会产生内存泄露风险

//    如果不删除ThreadLocal中用完的信息,会有内存泄露的风险
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserThreadLocal.remove();
    }
posted @ 2021-09-20 21:29  云鸽  阅读(94)  评论(0)    收藏  举报