个人博客--小小笔记2

目录

一、博客登录

1、JWT技术(03.md)

1.1、jwt介绍   

1.2、jwt使用

2、利用token(JWT)编写登录方法

2.1实现jwt

2.2jwt存放缓存redis中,以便后期检测是否登录

3、统一错误码,减少大量重复繁琐的返回值书写

二、获取用户信息和退出登录

1、获取用户信息

 2、退出登录

三、注册(04.md)需使用事务进行管理@Transactional

四、判断是否登录----拦截器||过滤器

1、拦截器:

2、过滤器:

五、ThreadLocal保存用户信息

*****************************内存泄漏05.md*****************************


一、博客登录

1、JWT技术(03.md)

1.1、jwt介绍   

jwt 可以生成 一个加密的token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。

请求需要登录的资源或者接口的时候,将token携带,后端验证token是否合法。

jwt 有三部分组成:A.B.C

A:Header,{"type":"JWT","alg":"HS256"} 固定

B:playload,存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息

C: 签证,A和B加上秘钥 加密而成,只要秘钥不丢失,可以认为是安全的。

jwt 验证,主要就是验证C部分 是否合法。

1.2、jwt使用

导入依赖包----编写工具类

工具类中涉及md5等加密方式、

自定义秘钥(jwtToken)private static final String jwtToken = "123456Mszlu!@#$$";、

检测是否合格checkToken(解码)

2、利用token(JWT)编写登录方法

2.1实现jwt

在实现类中要设置加密盐 private static final String slat = "mszlu!@#";

进行加密时,需要导入加密依赖,String pwd = DigestUtils.md5Hex(password + slat);

2.2jwt存放缓存redis中,以便后期检测是否登录

        //登录成功,使用JWT生成token,返回token和redis中
        String token = JWTUtils.createToken(sysUser.getId());
        redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);

3、统一错误码,减少大量重复繁琐的返回值书写

package com.mszlu.blog.vo;

public enum  ErrorCode {

    PARAMS_ERROR(10001,"参数有误"),
    ACCOUNT_PWD_NOT_EXIST(10002,"用户名或密码不存在"),
    NO_PERMISSION(70001,"无访问权限"),
    SESSION_TIME_OUT(90001,"会话超时"),
    NO_LOGIN(90002,"未登录"),;

    private int code;
    private String msg;

    ErrorCode(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

二、获取用户信息和退出登录

1、获取用户信息

前端返回值携带token,先手动检测token是否合法(是否登录或者其他未知情况),检测前端返回token在缓存中是否存在

 2、退出登录

清除缓存redis中存放的token即可

三、注册(04.md)需使用事务进行管理@Transactional

四、判断是否登录----拦截器||过滤器

1、拦截器:

自定义LoginInterceptor来判断token是否合法、在WebMVCConfig中添加拦截器

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 1. 需要判断请求的接口路径 是否为 HandlerMethod (controller方法),放行静态资源等请求,之拦截controller的方法
     * 2. 判断 token是否为空,如果为空 未登录
     * 3. 如果token 不为空,登录验证 loginService checkToken
     * 4. 如果认证成功 放行即可
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Autowired
    private TokenService tokenService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1. 需要判断请求的接口路径 是否为 HandlerMethod (controller方法)
        //instanceof-- 实例关系-- 判断handler请求是否和HandlerMethod (controller方法)为实例关系
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        //2. 判断 token是否为空,如果为空 未登录
        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.isBlank(token)){
            //未登录,进行拦截,返回信息
            R r = R.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            //返回给前端的信息为json,需要对上面信息处理
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(JSON.toJSONString(r));
            return false;
        }
        //3. 如果token 不为空,登录验证 loginService checkToken
        SysUser sysUser = tokenService.checkToken(token);
        if (sysUser == null){
            //未登录,进行拦截,返回信息
            R r = R.fail(ErrorCode.NO_LOGIN.getCode(), ErrorCode.NO_LOGIN.getMsg());
            //返回给前端的信息为json,需要对上面信息处理
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(JSON.toJSONString(r));
            return false;
        }
        //4. 如果认证成功 放行即可
        //我希望在controller中 直接获取用户的信息 怎么获取?
        UserThreadLocal.put(sysUser); //此方法利用本地线程,可直接获取sysUser
        return true;
    }

2、过滤器:

此处列举reggie项目的过滤器

package com.study.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.study.reggie.common.BaseContextUtil;
import com.study.reggie.common.R;
import com.sun.prism.impl.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 设置过滤名称:filterName = "LoginCheckFilter"
 * 设置过滤路径:urlPatterns = "/*"
 * 检查用户是否已经完成登录
 */
@Slf4j
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter { //自定义过滤器需要实现Filter
    //路径匹配器,支持通配符,后面需要用到这个判断两次路径是否相同
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //强制类型转换
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;

        //1、获取本次请求的URI
        String requestURI = request.getRequestURI();
        log.info("拦截到请求:{}",requestURI);
        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/login",
                "/user/sendMsg",
                "/doc.html",
                "/webjars/**",
                "/swagger-resources",
                "v2/api-docs"
        };
        //2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

        //3、如果不需要处理,则直接放行
        if (check){
            log.info("不需要处理,直接放行{}",requestURI);
            //利用filterChain进行放行
            filterChain.doFilter(request,response);
            return;
        }
        //4.1、判断客户端登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,id为{}",request.getSession().getAttribute("employee"));
            //获取当前用户的id,set到线程中,以便controller方法和Handler中使用
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContextUtil.setNowId(empId);

            filterChain.doFilter(request,response);
            return;
        }
        // 4-2、判断移动端登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("user") != null) {
            log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("user"));

            Long userId= (Long) request.getSession().getAttribute("user");

            BaseContextUtil.setNowId(userId);

            filterChain.doFilter(request, response);
            return;
        }
        //5、如果未登录则返回未登录结果,通过输出流方式向页面响应数据
        log.info("用户未登录");
        //此返回至客户端的数据NOTLOGIN需要跟前端保持一致,利用这个在前端实现页面跳转
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }
    /**
     * 利用路径匹配器判断两次路径是否相同
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls,String requestURI){
        //利用for循环遍历urls中的数据到url中,然后用PATH_MATCHER判断url和客户端请求路径是否一致
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match){
                return true;
            }
        }
        return false;
    }
}

五、ThreadLocal保存用户信息

为减少重复查询sql,应将查询之后的sysUser存放在线程中,以便后期调用

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();
    }
}

在调用完线程中的某参数时,应remove,否则可能会存在内存泄漏

*****************************内存泄漏05.md*****************************

编码细节,简要描述,时间不够在今天的笔记细说,在此做记录


long类型id精度丢失:
显示不出来的在ArticleVo中的id属性上面加上
@JsonSerialize(using = ToStringSerializer.class)

线程池保证两操作不干扰
写线程方法执行逻辑,写线程配置类开启多线程
@Async引入多线程........@EnableAsync开启对应线程

interger代替int
int基本类型在执行操作时会默认赋值0,所以在某些操作时若关联到int类型的属性,
但我们又不对其进行操作,最终结果mybatis还是会将int属性赋值0然后返回,需用interger代替int


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值