项目经验-注册登录


项目经验-用户注册-登录

注册流程

用户点击注册,系统将密码加密,然后保存到数据库。(这里没有校验用户重复情况)

注册

登录流程

用户登录,校验密码,生成 Token。

登录

校验流程

通过拦截器校验每次用户访问的 Token。

校验

扩展思考

其实这里也可以不使用 Shiro,而只是使用 SpringBoot 自身的拦截器去实现。

定义工具类:SecurityUtils

/**
 * SecurityUtils 工具类
 * 获取 Token 和密码加密
 */
public class SecurityUtils {

    /**
     * 获取请求token
     */
    public static String getToken() throws IllegalStateException {
        return getToken(Objects.requireNonNull(ServletUtils.getRequest()));
    }

    /**
     * 根据request获取请求token
     */
    public static String getToken(HttpServletRequest request) throws IllegalStateException{

        // 从 Hearder 中获取
        String header = request.getHeader(TokenConstants.AUTHENTICATION);

        // 从 Parameter 中获取
        if (StringUtils.isBlank(header)) {
            header = request.getParameter(TokenConstants.AUTHENTICATION);
        }

        // 从 Cookie 中获取
        if (StringUtils.isBlank(header)) {
            Cookie[] cookies = request.getCookies();
            if (ObjectUtils.isEmpty(cookies)) {
                throw new IllegalStateException("Request Token Is Empty");
            }
            for (Cookie cookie : cookies) {
                if (Objects.equals(cookie.getName(), TokenConstants.AUTHENTICATION)) {
                    header = cookie.getValue();
                }
            }
        }

        // 都为空的情况
        if (StringUtils.isBlank(header)) {
            throw new IllegalStateException("Request Token Is Empty");
        }

        if (!header.startsWith(TokenConstants.PREFIX)) {
            throw new IllegalStateException("Request Token Is Empty");
        }

        return JwtUtils.getUserKey(header.substring(TokenConstants.PREFIX.length()));
    }

    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password)
    {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    /**
     * 判断密码是否相同
     *
     * @param rawPassword 真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword, String encodedPassword)
    {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
}

定义工具类:JwtUtils

/**
 * Jwt工具类
 */
public class JwtUtils {

    public static String secret = TokenConstants.SECRET;

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    public static String createToken(Map<String, Object> claims)
    {
        return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    public static Claims parseToken(String token)
    {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * 根据令牌获取用户标识
     * 
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserKey(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据令牌获取用户标识
     * 
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserKey(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据令牌获取用户ID
     * 
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserId(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_ID);
    }

    /**
     * 根据身份信息获取用户ID
     * 
     * @param claims 身份信息
     * @return 用户ID
     */
    public static String getUserId(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_ID);
    }

    /**
     * 根据令牌获取用户名
     * 
     * @param token 令牌
     * @return 用户名
     */
    public static String getUserName(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_PHONE);
    }

    /**
     * 根据身份信息获取用户名
     * 
     * @param claims 身份信息
     * @return 用户名
     */
    public static String getUserName(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_PHONE);
    }

    /**
     * 根据身份信息获取键值
     * 
     * @param claims 身份信息
     * @param key 键
     * @return 值
     */
    public static String getValue(Claims claims, String key)
    {
        return Convert.toStr(claims.get(key), "");
    }
}

定义拦截器:LoginInterceptor

/**
 * 接口拦截器
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Resource
    private RedisUtils redisUtils;

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

        // 先从缓存中获取数据
        String token = SecurityUtils.getToken(request);
        String key = RedisConstants.LOGIN_TOKEN_KEY + token;
        LoginUser loginUser = (LoginUser) redisUtils.get(key);
        if (ObjectUtils.isNotEmpty(loginUser)) {
            LoginUserUtils.set(loginUser);
            return true;
        } else {
            throw new IllegalStateException("User Token Is Expires");
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) {
        LoginUserUtils.remove();
    }
}

启用拦截器:LoginConfig

/**
 * 启用拦截器
 */
@Configuration
public class LoginConfig implements WebMvcConfigurer {

    @Resource
    private LoginInterceptor loginInterceptor;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(loginInterceptor).addPathPatterns("/api/**")
               .excludePathPatterns("/api/user/login");
    }

    /**
     * 显示 swagger-ui.html文档展示页,还必须注入 swagger 资源
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

注册登录流程

注册-登录

验证校验流程

验证码校验

业务请求流程

业务请求Token

  • 参考

https://juejin.cn/post/7158077107958972424

https://juejin.cn/post/7025768845075808286


文章作者: L Q
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 L Q !
  目录