文章介绍
项目A跳转至项目B,项目A在以登录的情况下,登录项目B时还需要输入项目B的帐号,用户体验不好。因此需要将两个项目绑定,项目A登陆后向项目B跳转时可直接登录。本项目是维护别人的老系统,是使用jsp开发的,虽然用的技术比较老,但是整个流程的思想还是挺不错的。
业务流程分析
- 项目A向项目B跳转时,携带ticket
- 项目B获取到ticket后,解析出用户信息
- 根据用户信息判断数据库中是否存在该用户,若存在,返回前端可直接登录项目B状态。不存则插入,错误则返回前端不可直接登录项目B状态
- 使用session存用户状态,因为是jsp项目,使用技术比较老。
- 前端根据状态进行跳转,成功则重定向系统主页,失败则停留在登录页。
获取ticket并解析
项目A-项目B,原系统是跳转到登录页,在这里我封装了一个方法,当项目B登录页加载时,调用后端解析ticket的方法。
获取ticket后,将appid,secret,ticket三者经MD5
加密后形成signature,之后调用第三方接口获取用户的信息。
注意:
参数在路径上进行传递时,可能会被转义。我们通过getParameter()方法获取真实的参数,用于md5加密。
使用getQueryString()方法获取未被解析的参数,进行第三方接口的访问。
校验ticket接口
@ResponseBody
@RequestMapping(value = "/login/verify", method = {RequestMethod.GET})
public boolean verifySignature(HttpServletRequest request) throws UnsupportedEncodingException, NoSuchAlgorithmException {
logger.info("校验signature");
String ticket = request.getParameter("ticket").replace("ticket=", "");
String secret = "2nN5Dd+Ehz9mJ06sb7+GpvNNyI01Fzr9k7XLmd+imu4=";
String appId = "104203";
String pass = ticket + appId + secret;
String signature = encodeMd5(pass);
//TODO 调用第三方
String url = "http://www.ilab-x.com/open/api/v2/token?" +
"ticket=" + request.getQueryString().replace("ticket=","") +
"&appid=" + appId +
"&signature=" + signature;
System.out.println(url);
JSONObject currentUserResult = sendRequest(url);
JSONObject currentUserResult = sendRequest(url);
String un = currentUserResult.getString("un");
String dis = currentUserResult.getString("dis");
String contextPath = request.getContextPath();
//用户存在则返回用户,不存在返回null
AdminEntity adminEntity = adminService.existAdminNameAndReturnAdmin(un);
if (adminEntity != null) {
httpSession.setAttribute("admin", adminEntity);
return true;
} else {
//创建用户 密码默认为123456
adminEntity = new AdminEntity();
adminEntity.setNickname(un);
adminEntity.setPhoneNumber("");
adminEntity.setEmail("");
adminEntity.setCompany("");
adminEntity.setSex(1);
adminEntity.setRole(1);
adminEntity.setVisible(1);
adminEntity.setPassword("123456");
//数据库不存在进行插入
int status = adminService.register(adminEntity);
if (status > 0) {
//免登录成功
//通过session记录用户的登录状态
httpSession.setAttribute("admin", adminEntity);
return true;
} else {
return false;
}
}
}
发送第三方请求封装
根据校验结果进行重定向跳转,如果校验成功,跳转B系统,校验失败,跳转B系统登录页。
public JSONObject sendRequest(String url) {
HttpURLConnection conn = null;
try {
URL serverUrl = new URL(url);
conn = (HttpURLConnection) serverUrl.openConnection();
conn.setRequestMethod("GET");
//设置连接超时时间和读取超时时间
conn.setConnectTimeout(15000);
conn.setReadTimeout(60000);
conn.setRequestProperty("Accept", "application/json");
//必须设置false,否则会自动redirect到重定向后的地址
conn.setInstanceFollowRedirects(false);
//发送请求
conn.connect();
} catch (Exception e) {
e.printStackTrace();
}
//获取返回值
return getHttpReturn(conn);
}
//获取请求返回值
public JSONObject getHttpReturn(HttpURLConnection connection) {
//将返回的输入流转换成字符串
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
String result = "";
try {
if (200 == connection.getResponseCode()) {
StringBuffer buffer = new StringBuffer();
inputStreamReader = new InputStreamReader(connection.getInputStream(), "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
result = buffer.toString();
} else {
System.out.println("ResponseCode is an error code:" + connection.getResponseCode());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connection.disconnect();
}
JSONObject res = new JSONObject(result);
logger.info("【获取signature】"+res);
return res;
}
MD5加密
32位大写加密
public String encodeMd5(String pass) throws NoSuchAlgorithmException, UnsupportedEncodingException {
//md5大写加密
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update((pass).getBytes("UTF-8"));
byte b[] = md5.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
return buf.toString().toUpperCase();
}
前端页面跳转
其实最初的实现方案是校验和跳转是在一个接口中完成的,接口直接返回对应页面的字符串进行页面重定向,但是因为ajax不支持页面重定向,只支持页面刷新,这样虽然后端指明重定向,但是实际还是停留在原页面,因此使用了校验的页面分开的方式。
window.onload = function () {
var ticket = getUrlParams(window.location.href, "ticket");
var basePath=$('#basePath').attr("value");
var path=$('#path').attr("value");
console.log("path"+path)
console.log("bathPath"+basePath)
if (ticket != null && ticket != "") {
$.ajax({
//校验target
url: path+"/login/verify?ticket=" + ticket,
type: "GET",//请求方式
//参数设置
// data:"username=tom&age=18",
响应成功后的回调函数
data: {},
//请求成功
success: function (data) {
console.log(data)
if (data == true) {
window.location.href = basePath+"/login/avoidLogin";
} else {
window.location.href = basePath;
}
},
//请求失败
error: function () {
window.location.href = basePath;
},
//设置接收到的响应数据的格式
dateType: "text"
})
}
};
jsp中获取当前请求地址
<basePath value = "<%=path%>" id = "path"></basePath>
var path=$('#path').attr("value");
jsp中获取当前请求路径
<basePath value = "<%=basePath%>" id = "basePath"></basePath>
var basePath=$('#basePath').attr("value");
js获取当前url地址
window.location.href
js获取请求路径中的参数
var ticket = getUrlParams(window.location.href, "ticket");
function getUrlParams(url, name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)")
const cuturl = url.substr(url.indexOf("?") + 1)
const matchArray = cuturl.match(reg)
if (matchArray) {
return decodeURI(matchArray[2])
}
return null;
};
验证成功后,免登录处理
因为再次请求接口时,我们使用的是js的重定向,并非发送的ajax请求,因此我们可以在后端通过返回字符串直接重定向到指定页面。
@RequestMapping(value = "/login/avoidLogin", method = {RequestMethod.GET})
public String avoidLogin(HttpServletRequest request, HttpServletResponse response, final RedirectAttributes redirectAttributes, Model model) throws Exception {
logger.info("进行免登录");
AdminEntity admin = (AdminEntity) httpSession.getAttribute("admin");
String contextPath = request.getContextPath();
if (admin != null) {
//登陆成功,跳转到首页。
redirectAttributes.addFlashAttribute("css", "success");
redirectAttributes.addFlashAttribute("msg", "登录成功!");
if (admin.getRole() == Constants.USER) {//普通用户
//model.addAttribute("to", "adminDetail");
model.addAttribute("to", "myproject");
} else if (admin.getRole() == Constants.SUPERADMIN || admin.getRole() == Constants.ADMIN) {//普通管理员、系统管理员
//model.addAttribute("to", "adminList");
model.addAttribute("to", "myproject");
}
return "manager/index";//跳转到后台首页
} else {
if (LoginInterceptor.verifyCert(request) == false) {
System.out.println("certFlag: 证书无效");
response.sendRedirect(contextPath + "/cert.jsp");
} else {
System.out.println("certFlag: 证书有效");
}
return "manager/homepage";
}
// }
}