起源
作为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验证之后进行的流程不太满意,应该和哪些模块进行融合之类的,之后再说吧。