开发通行证服务

个人复习使用 ,精简版

开发通行证主要包含:阿里云第三方服务发送验证码以及验证码校验功能,用户一键登录/注册功能,用户信息查询功能(数据库访问优化),分布式会话功能,会话拦截、状态激活拦截功能与AOP警告日志监控功能。

 

阿里云第三方服务发送验证码以及验证码校验功能

发送验证码:

        controller层:

    @Override
    public GraceJSONResult getSMSCode(String mobile, HttpServletRequest request) {

        //获取用户ip
        String userIp = IPUtil.getRequestIp(request);

        //根据用户的ip进行限制,限制用户在60秒内只能获得一次验证码
        redis.setnx60s(MOBILE_SMSCODE + ":" + userIp, userIp);

        //生成随机验证码并发送短信
        String random = (int) ((Math.random() * 9 + 1) * 100000) + "";
        smsUtils.sendSMS(mobile, random);

        //把验证码存入redis,用于后续进行验证
//        redis.set(MOBILE_SMSCODE + ":" + mobile, random, 30 * 60);
        redis.set(MOBILE_SMSCODE + ":" + mobile, random);

        return GraceJSONResult.ok();
    }
  1. 限制短信发送频率:将ip信息写入缓存,60s失效。后续配置拦截器。
  2. 随机生成验证码,写入缓存,30min失效。并发送短信。

配置拦截器(访问controller前):判断缓存中是否存在ip信息

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

        //获取用户ip
        String userIp = IPUtil.getRequestIp(request);

        boolean keyIsExist = redis.keyIsExist(MOBILE_SMSCODE + ":" + userIp);

        if(keyIsExist){
            //TODO
            GraceException.display(ResponseStatusEnum.SMS_NEED_WAIT_ERROR);//把错误信息抛给前端,抛的是exception,而不是json数据
//            System.out.println("短信发送频率太高");
            return false;
        }
        /**
         * false:请求被拦截
         * true:请求通过验证,放行
         */
        return true;
    }

注册拦截url:

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Bean
    public PassportInterceptor getPassportInterceptor() {
        return new PassportInterceptor();
    }

    //注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(getPassportInterceptor())
                .addPathPatterns("/passport/getSMSCode");
    }
}

注册、验证码校验:

    @Override
    public GraceJSONResult doLogin(@Valid RegistLoginBO registLoginBO,
                                   BindingResult bindingResult,
                                   HttpServletRequest request,
                                   HttpServletResponse response) {

        //0.判断BindingResult是否保存了验证错误的信息,如果有,那么需要返回
        if (bindingResult.hasErrors()) {
            Map<String, String> map = getErrors(bindingResult);
            return GraceJSONResult.errorMap(map);
        }

        //1.校验验证码是否匹配
        String mobile = registLoginBO.getMobile();//前端传来的用户输入信息
        String smsCode = registLoginBO.getSmsCode();

        String redisSMSCode = redis.get(MOBILE_SMSCODE + ":" + mobile);
        if (StringUtils.isBlank(redisSMSCode) || !redisSMSCode.equalsIgnoreCase(smsCode)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);
        }

        //2.查询数据库,判断该用户注册
        AppUser user = userService.queryMobileIsExist(mobile);
        if (user != null && user.getActiveStatus() == UserStatus.FROZEN.type)  {
            //如果用户存在,并且该用户被冻结,那么直接抛出异常,禁止登录
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_FROZEN);
        }else if (user == null){
            //如果用户没有注册过,则从mysql查询到的对象为null,需要将注册信息入库。
            user = userService.createUser(mobile);
        }

        //3.保存用户分布式会话的相关操作
        int userActiveStatus = user.getActiveStatus();
        if(userActiveStatus != UserStatus.FROZEN.type) {
            //保存token和user(json对象)到redis
            String uToken = UUID.randomUUID().toString().trim();
            redis.set(REDIS_USER_TOKEN + ":" +user.getId(),uToken);
            redis.set(REDIS_USER_INFO + ":" +user.getId(), JsonUtils.objectToJson(user));

            //保存用户token和id到cookie中
            setCookie(request,response,"utoken",uToken,COOKIE_MONTH);
            setCookie(request,response,"uid",user.getId(),COOKIE_MONTH);
        }
//        redis.del(MOBILE_SMSCODE + ":" + mobile);
        //用户存在,状态正常。
        return GraceJSONResult.ok(userActiveStatus);
    }
  1. 对输入和redis验证码判空(@Valid和BindingResult )
  2. 判断与redis中是否相等->写入数据库或直接登录(无redis)
  3. 分布式会话:生成token->写入redis、写入cookie,后续设置拦截器验证token。

设置拦截器验证token:先判空(返回未登录),再核对(返回会话失效)

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

        //也可以用cookie,但是安卓和小程序上没有cookie,因此用以下方法获token和id
        String userId = request.getHeader("headerUserId");
        String userToken = request.getHeader("headerUserToken");

        //判断是否放行
        Boolean run = verifyUserIdToken(userId, userToken,REDIS_USER_TOKEN);
        System.out.println(run);

        /**
         * false:请求被拦截
         * true:请求通过验证,放行
         */
        return true;
    }
    public boolean verifyUserIdToken(String id,
                                     String token,
                                     String redisKeyPrefix){
        if (StringUtils.isNotBlank(id)&&StringUtils.isNotBlank(token)){
            String redisToken = redis.get(redisKeyPrefix+":"+id);
            if(StringUtils.isBlank(id)){
                GraceException.display(ResponseStatusEnum.UN_LOGIN);
            }else {
                if (!redisToken.equalsIgnoreCase(token)){
                    GraceException.display(ResponseStatusEnum.TICKET_INVALID);
                    return false;
                }
            }
        }else {
            GraceException.display(ResponseStatusEnum.UN_LOGIN);
            return false;
        }
        return true;
    }

用户信息查询功能(数据库访问优化)

先查询redis,若没有再查数据库,接着存redis。

 @Override
    public GraceJSONResult getAccountInfo(String userId) {
        //0.判断参数不能为空
        if(StringUtils.isBlank(userId)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }

        //1.根据userId查询用户信息
        AppUser user = getUser(userId);

        //2.返回用户信息(有些信息不用展示在前端)
        UserAccountInfoVO userAccountInfo = new UserAccountInfoVO();
        BeanUtils.copyProperties(user, userAccountInfo);

        return GraceJSONResult.ok(userAccountInfo);
    }

http://t.csdn.cn/358Ncicon-default.png?t=N2N8http://t.csdn.cn/358Nc缓存双写:

http://t.csdn.cn/rcXagicon-default.png?t=N2N8http://t.csdn.cn/rcXag

AOP警告日志监控

对service加环绕通知,若执行时间过长,返回一个错误。

统一异常拦截:面向aop


/**
 * 统一异常拦截处理
 * 可以针对异常的类型进行捕获,然后返回json信息到前端
 */
@ControllerAdvice//面向aop
public class GraceExceptionHandler {

    @ExceptionHandler(MyCustomException.class)
    @ResponseBody
    public GraceJSONResult returnMyException(MyCustomException e){
        return GraceJSONResult.exception(e.getResponseStatusEnum());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偷偷吃小孩.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值