cas统一认证实现

起源

作为SDUOJ开发组的一员,承担自己的责任与义务,为sduoj添枝加叶。

了解原理

腾哥给我讲了原理,
很棒的图
上面这个图生动形象。

理解了原理,腾哥又发了一些对应的接口,我就开始试着写一写,写这个比gateway限流轻松了很多,不需要查阅很多的文档和看一坨坨的源码(尽管他们写的很好)。

最开始的一版业务基本都写到了Controller层,非常的不健康和不友好,也体现了我菜狗一般的业务逻辑。

后来腾哥跟我说最好抽离一下,为之后接入QQ和微信做准备,然后我就把很多代码都抽离到了server层。

cas验证没什么原理好讲的,就是一条线写下来。

@GetMapping("/xxx")
    @ResponseBody
    public ResponseResult<UserSessionDTO> sduCasLogin(HttpServletResponse response,
                            @RequestParam(value = "ticket",required = true) String ticket,
                            @RealIp String ipv4,
                            @RequestHeader("user-agent") String userAgent) throws IOException {

        //获得学号
        HashMap<String,String> map = new HashMap<>();
        map.put("service","xxx");
        map.put("ticket",ticket);
        String id = userService.getDataByMap("对应cas验证网站",map).
                split("<cas:ID_NUMBER>")[1].substring(0,12);
        return userService.WeatherBinding(id,"cas",response,ipv4,userAgent);
    }

    @PostMapping("/CasBinding")
    @ResponseBody
    public ResponseResult<UserSessionDTO> CasBinding(HttpServletResponse response,
                                                @RequestBody @NotNull Map<String, String> json,
                                                @RealIp String ipv4,
                                                @RequestHeader("user-agent") String userAgent) throws ApiException {

       return userService.BindingAcount(response,json,ipv4,userAgent);
    }

    @GetMapping("/CasUnbinding")
    @ApiResponseBody
    public boolean CasUnbinding(@UserSession UserSessionDTO userSessionDTO) {
        return userService.Unbinding(userSessionDTO.getUsername(),UserDO::getSduId);
    }

 public String getDataByMap(String Tourl, HashMap<String,String>pro) {
        String url = Tourl+"?";
        for (String key:pro.keySet()) {
            url = url + key + "=" + pro.get(key) + "&";
        }HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        String strbody=restTemplate.exchange(url, HttpMethod.GET, entity,String.class).getBody();
        return strbody;
    }
    //对于每一种绑定方式写一个实体类
    public ResponseResult<UserSessionDTO> WeatherBinding(String id,
                                                          String type,
                                                          HttpServletResponse response,
                                                          @RealIp String ipv4,
                                                          @RequestHeader("user-agent") String userAgent) throws IOException {
        UserDO userDO ;
        try{//已绑定过进行登录
            userDO = IfBindingGetDO(UserDO::getSduId,id);
            UserSessionDTO userSessionDTO = BindingLogin(userDO,ipv4,userAgent);
            response.setHeader(UserSessionDTO.HEADER_KEY, JSON.toJSONString(userSessionDTO));
            return ResponseResult.ok(userSessionDTO);//登录成功返回ok
        }
        catch (ApiException e) {
            //重定向进行账号绑定,重定向的时候发送对应的验证信息,用户在前端完成绑定之后发送这两个参数后端验证
            String uuid = BindingRedis(id,type);
            //重定向到对应的前端绑定页面
            response.sendRedirect("/......?uuid="+uuid+"&student_id="+id);
            return ResponseResult.error();
        }
    }
    public ResponseResult<UserSessionDTO> BindingAcount(HttpServletResponse response,
                                                     @RequestBody @NotNull Map<String, String> json,
                                                     @RealIp String ipv4,
                                                     @RequestHeader("user-agent") String userAgent) throws ApiException {

        String username = null, password = null, student_id = null, uuid = null;
        try {
            username = json.get("username");
            password = json.get("password");
            student_id = json.get("student_id");
            uuid = json.get("uuid");
        } catch (Exception ignore) {
        }
        SFunction<UserDO, ?> sFunction = null;//判断是哪种登录方式
        switch (uuid.substring(0,2))
        {
            case "Ca":  sFunction = UserDO::getSduId;
        }
        if (!CasUuidIsTrue(student_id, uuid)) return ResponseResult.error();  //先验证uuid是否正确
        if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
            UserSessionDTO userSessionDTO = login(username, password, ipv4, userAgent);// 登录校验
            response.setHeader(UserSessionDTO.HEADER_KEY, JSON.toJSONString(userSessionDTO));
            if (SetBinding(sFunction,student_id, username)) return ResponseResult.ok(userSessionDTO); //验证成功,进行绑定
        }
        return ResponseResult.error();
    }

 public @NotNull UserDO IfBindingGetDO(SFunction<UserDO, ?> sFunction, String id) throws ApiException {
        // 查询
        LambdaQueryChainWrapper<UserDO> query = userDao.lambdaQuery();
        query.like(sFunction, id);
        UserDO userDO = query.one();
        AssertUtils.notNull(userDO, ApiExceptionEnum.USER_NOT_FOUND);
        return userDO;
    }

    public UserSessionDTO BindingLogin(UserDO userDO,String ipv4,String userAgent) {

        UserSessionDO userSessionDO = UserSessionDO.builder()
                .ipv4(ipv4)
                .userAgent(userAgent)
                .success(0)
                .build();
        userSessionDO.setUsername(userDO.getUsername());
        userSessionDO.setSuccess(1);
        List<Long> groupIdList = groupService.userIdToGroupIdList(userDO.getUserId());
        userSessionDao.save(userSessionDO);
        return userSessionConverter.to(userDO, userSessionDO, groupIdList);
    }
    public String BindingRedis(String student_id,String sour){
        String uuid = UUID.randomUUID().toString();
        switch (sour)
        {
            case "cas":redisUtils.set(RedisConstants.getCasBindingKey(uuid),student_id,userServiceProperties.getCasBindingTime());
        }
        return uuid;
    }
    public boolean CasUuidIsTrue(String student_id,String uuid){
        String get_id = redisUtils.get(uuid).toString();
        return StringUtils.equals(get_id,student_id);
    }
    public boolean SetBinding(SFunction<UserDO, ?> sFunction,String id,String username){
        UserDO userDO = null;
        userDO = userDao.lambdaQuery().eq(UserDO::getUsername, username).one();
        //在设定之前查验一下future确保没有字段,防止出现一个账号绑定多个学号情况
        if(StringUtils.isNotBlank((String)sFunction.apply(userDO))) return false;
        LambdaUpdateChainWrapper<UserDO> update = userDao.lambdaUpdate()
                .eq(UserDO::getUsername, username)
                .set(sFunction,id);
        return update.update();
    }
    public boolean Unbinding(String username,SFunction<UserDO, ?> sFunction){
        LambdaUpdateChainWrapper<UserDO> update = userDao.lambdaUpdate()
                .eq(UserDO::getUsername, username)
                .set(sFunction,null);
        return update.update();
    }

但是腾哥一直对cas验证之后进行的流程不太满意,应该和哪些模块进行融合之类的,之后再说吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值