登录之后的二次认证

需求

二次认证,本来的需求是完成一个二次认证,对于登录到制定系统的中的用户进行二次验证,本来是只有某一个功能要用的,也是按照某一个功能设计的接口情况,但是在这个接口上线之前要求改成通用性的。

处理

首先是简单的处理,只有某一个功能用的话就涉及三个接口,第一个接口去感知这个用户是否二次认证过,第二个接口是用来发送验证码,第三个接口是将获得验证码输入进行匹配。我是这么想的也是这么实现的。

发送验证码
    @Override
    public Map SendVerificationCodeRetCode(Map<String, Object> params) {
        Map<String, Object> ret = new HashMap<>();
        String usernumber = String.valueOf(params.get("usernumber"));
        if(StringUtils.isBlank(usernumber)){
            ret.put("status","1");
            ret.put("msg","参数不全");
            return ret;
        }

        boolean existslogin = redisUtils.exists(getForbiddenToLoginKey(usernumber));
        if(existslogin){
            ret.put("status","2");
            ret.put("msg","验证码输入错误次数超过5次,请两小时后重试。");
            return ret;
        }

        boolean exists = redisUtils.exists(getSendTimeKey(usernumber));
        if(exists){
            ret.put("status","3");
            ret.put("msg","发送过验证码,请稍后再试");
            return ret;
        }else {
            String generate = generate();
            redisUtils.set(getSendTimeKey(usernumber),generate,SendVerificationCodeTimeEffective);
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String formattedDateTime = now.format(formatter);
            String text = "由XXX发送,验证码:"+generate+",验证码有效期1分钟,切勿将验证码泄露于他人。发送时间:"+formattedDateTime;
            params.put("msgcontent",text);
            params.put("domainOrPayload",2);
            secondaryValidation(params);
            ret.put("status","4");
            ret.put("data",generate);
            ret.put("msg","验证码发送成功");
            ret.put("text",text);
            return ret;
        }
    }
验证码验证
    @Override
    public Map VerifyVerificationCode(Map<String, Object> params, HttpServletResponse res) {

        Map<String, Object> ret = new HashMap<>();
        String usernumber = String.valueOf(params.get("usernumber"));
        String code = String.valueOf(params.get("code"));

        if(StringUtils.isBlank(usernumber) || StringUtils.isBlank(code)){
            ret.put("status","1");
            ret.put("msg","参数不全");
            return ret;
        }

        boolean existslogin = redisUtils.exists(getForbiddenToLoginKey(usernumber));
        if(existslogin){
            ret.put("status","2");
            ret.put("msg","验证码输入错误次数超过5次,请两小时后重试");
            return ret;
        }

        boolean exists = redisUtils.exists(getSendTimeKey(usernumber));
        if(exists){
            String usernumberCode = String.valueOf(redisUtils.get(getSendTimeKey(usernumber)));
            if(usernumberCode.equals(code)){
                redisUtils.set(getVerificationCodeKey(usernumber),"1",loginTime);
                boolean existsFalseTime = redisUtils.exists(getFalseTimeKey(usernumber));
                if(existsFalseTime){
                    redisUtils.remove(getFalseTimeKey(usernumber));
                }
                ret.put("status","3");
                ret.put("msg","验证通过");

                String secondary_validation = UUID.randomUUID().toString();

                Cookie cookie = new Cookie("secondary_validation", secondary_validation);
                cookie.setPath("/");
                cookie.setDomain("");
                cookie.setHttpOnly(Boolean.TRUE);
                res.addCookie(cookie);
                redisUtils.set(getVerificationCodeKeyCookie(unifastContext.getUser().getStaffId()),secondary_validation,loginTime);

                return ret;
            }else{
                boolean existsFalseTime = redisUtils.exists(getFalseTimeKey(usernumber));
                if(existsFalseTime){
                    int falseTime = (int) redisUtils.get(getFalseTimeKey(usernumber));
                    falseTime = falseTime + 1;
                    if(falseTime > 5){
                        redisUtils.remove(getFalseTimeKey(usernumber));
                        redisUtils.set(getForbiddenToLoginKey(usernumber),"1",ForbiddenToLoginTime);
                        ret.put("status","4");
                        ret.put("msg","验证码输入不正确,请重新输入。");
                        return ret;
                    }else {
                        redisUtils.set(getFalseTimeKey(usernumber),falseTime);
                        ret.put("status","5");
                        ret.put("msg","验证码输入不正确,请重新输入。");
                        return ret;
                    }

                }else{
                    redisUtils.set(getFalseTimeKey(usernumber),1,falseTimeTime);
                    ret.put("status","6");
                    ret.put("msg","验证码输入不正确,请重新输入。");
                    return ret;
                }
            }
        }else{
            ret.put("status","7");
            ret.put("msg","验证码已失效,请重新发送。");
            return ret;
        }
    }
判断是否登录
    @Override
    public Map WhetherToValidate(Map<String, Object> params) {
        Map<String, Object> ret = new HashMap<>();
        String usernumber = String.valueOf(params.get("usernumber"));
        if(StringUtils.isBlank(usernumber)){
            ret.put("status","1");
            ret.put("msg","参数不全");
            return ret;
        }
        if(redisUtils.exists(getVerificationCodeKey(usernumber))){
            ret.put("status","2");
            ret.put("msg","已验证");
            return ret;
        }else{
            ret.put("status","3");
            ret.put("msg","未验证");
            return ret;
        }
    }

三个接口写完了,感觉是完事了,但是存在一个问题,如果被人直接访问了呢,直接选择跳过前台的二次验证判断接口,于是就想写一个拦截器,直接在每一个请求之前进行判断,思路的话就是在的登录之后,在token中存一个cookie中存一个key用于表示

import cn.chinaunicom.sdsi.cloud.auth.MallUser;
import cn.chinaunicom.sdsi.cloud.system.permission.entity.SysPermissionPO;
import cn.chinaunicom.sdsi.framework.utils.RedisUtils;
import cn.chinaunicom.sdsi.framework.utils.UnifastContext;
import cn.chinaunicom.sdsi.system.service.SecondaryValidationMenuService;
import cn.chinaunicom.sdsi.talent.secondaryValidation.service.SecondaryValidationService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Primary
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ConfigurationProperties(prefix = "unifast.cloud.resource")
public class SecondaryValidationFilter implements HandlerInterceptor {

    @Autowired
    private SecondaryValidationMenuService secondaryValidationService;
    @Autowired
    private UnifastContext unifastContext;
    @Resource
    private RedisUtils redisUtils;


    public static final String SECONDARY_VALIDATION = "secondary_validation";

    @Getter
    @Setter
    private List<String> whiteList;

    /**
     * 在请求处理之前调用-Controller方法调用之前调用
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String s : whiteList) {
            if (pathMatcher.match(s, request.getRequestURI())) {
                return true;
            }
        }
        MallUser user = unifastContext.getUser();
        //获取需要验证的接口
        List<SysPermissionPO> permissionList = secondaryValidationService.getMoreVerify();
        for (SysPermissionPO permission : permissionList) {
            if(permission.getCheckCode().contains(request.getRequestURI())){
                String cellphone = user.getCellphone();
                Boolean isCode = redisUtils.exists("verification_code:" + cellphone);
                if(isCode){
                    return true;
                }else{
                    final Cookie[] cookies = request.getCookies();
                    if (cookies != null && cookies.length > 0) {
                        for (Cookie cookie : cookies) {
                            if ((SECONDARY_VALIDATION+":"+user.getStaffId()).equals(cookie.getName())) {
                                //获取redis中的token并判断正确性
                                String secondaryCalidationCode = (String) redisUtils.get(SECONDARY_VALIDATION + ":" + user.getStaffId());
                                if(secondaryCalidationCode.equals(cookie.getValue())){
                                    return true;
                                }else{
                                    response.setCharacterEncoding("utf-8");
                                    response.setHeader("Content-Type","application/json;charset=utf-8");
                                    String json = "{\"message\": \"需要二次认证\",\"code\": \"0\",\"data\": \"null\",\"success\": false}";
                                    try {
                                        response.getWriter().write(json);
                                    } catch (IOException e) {
                                        throw new RuntimeException(e);
                                    }
                                    return false;
                                }
                            }
                        }
                    }
                    response.setCharacterEncoding("utf-8");
                    response.setHeader("Content-Type","application/json;charset=utf-8");
                    String json = "{\"message\": \"需要二次认证\",\"code\": \"0\",\"data\": \"null\",\"success\": false}";
                    try {
                        response.getWriter().write(json);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 请求处理之后调用,但是在视图被渲染之前-Controller方法调用之后
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }

    /**
     * 在整个请求调用之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行 主要是用于进行资源清理工作
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}
import cn.chinaunicom.sdsi.filter.SecondaryValidationFilter;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.annotation.Resource;
import java.util.List;

@Order(100)
@Configuration
/**
 * 二次验证拦截器

 */
public class SecondaryValidation extends WebMvcConfigurerAdapter {


    @Resource
    private SecondaryValidationFilter loginLoginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePathList = Lists.newArrayList();
        registry.addInterceptor(loginLoginInterceptor).addPathPatterns("/*/**").excludePathPatterns(excludePathList.toArray(new String[0]))
                .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");

        super.addInterceptors(registry);
    }
}

实话说这块不是我写的,但是我大概能够看的懂,但是我自己写可能还是有差距,持续学习吧

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

又是重名了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值