SSO 单点登录系统
1. taotao-sso-service
工程搭建
需要创建一个sso服务工程,可以参考taotao-manager创建。
Taotao-sso(pom聚合工程)
|–taotao-sso-interface(jar)
|–taotao-sso-Service(war)
框架整合mybatis、jedis、sping
2. taotao-sso-web
工程搭建
表现层工程包含登录和注册页面,需要调用sso服务实现。
给app提供服务,restful形式的服务。
Taotao-sso-web(war包)
框架整合 sping\springMvc
3. 注册
http://localhost:8084/page/register
密码加密
UserServiceImpl.register()
//密码要进行md5加密
String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(md5Pass);
3. 登录
http://localhost:8084/page/login
UserController.login()
验证成功后,token写入cookie
//登录成功后写cookie
if (result.getStatus() == 200) {
//把token写入cookie
CookieUtils.setCookie(request, response, TOKEN_KEY, result.getData().toString());
}
UserServiceImpl.login()
密码校验
//密码要进行md5加密然后再校验
if (!DigestUtils.md5DigestAsHex(password.getBytes())
.equals(user.getPassword())) {
//返回登录失败
return TaotaoResult.build(400, "用户名或密码不正确");
}
生成token,存入redis
//生成token,使用uuid
String token = UUID.randomUUID().toString();
//清空密码
user.setPassword(null);
//把用户信息保存到redis,key就是token,value就是用户信息
jedisClient.set(USER_SESSION + ":" + token, JsonUtils.objectToJson(user));
//设置key的过期时间
jedisClient.expire(USER_SESSION + ":" + token, SESSION_EXPIRE);
//返回登录成功,其中要把token返回。
return TaotaoResult.ok(token);
4. 通过token查询用户信息
请求 http://localhost:8084/user/token/dcc49643-6f64-4d50-a4dc-50cc9b85b2ef
返回
{“status”:200,“msg”:“OK”,“data”:{“id”:37,“username”:“test1”,“password”:null,“phone”:“123”,“email”:null,“created”:1633447009000,“updated”:1633447009000}}
功能分析
请求的url:/user/token/{token}
参数:String token需要从url中取。
返回值:json数据。使用TaotaoResult包装Tbuser对象。
业务逻辑:
1、从url中取参数。
2、根据token查询redis。
3、如果查询不到数据。返回用户已经过期。
4、如果查询到数据,说明用户已经登录。
5、需要重置key的过期时间。
6、把json数据转换成TbUser对象,然后使用TaotaoResult包装并返回。
首页展示用户名(ajax请求跨域 json)
1、当用户登录成功后,在cookie中有token信息。
2、从cookie中取token根据token查询用户信息。
3、把用户名展示到首页。
方案一:在Controller中取cookie中的token数据,调用sso服务查询用户信息。
方案二:当页面加载完成后使用js取token的数据,使用ajax请求查询用户信息。
问题:服务接口在sso系统中。Sso.taotao.com(localhost:8088),在首页显示用户名称,首页的域名是www.taotao.com(localhost:8082),使用ajax请求跨域了。
Js不可以跨域请求数据。
什么是跨域:
1、域名不同
2、域名相同端口不同。
解决js的跨域问题可以使用jsonp。
Jsonp不是新技术,跨域的解决方案。使用js的特性绕过跨域请求。Js可以跨域加载js文件。
Jsonp原理
Json实现
客户端 taotao.js
var TT = TAOTAO = {
checkLogin : function(){
var _ticket = $.cookie("TT_TOKEN");
if(!_ticket){
return ;
}
$.ajax({
url : "http://localhost:8084/user/token/" + _ticket,
dataType : "jsonp",
type : "GET",
success : function(data){
if(data.status == 200){
var username = data.data.username;
//var html = username + ",欢迎来到淘淘!<a href=\"http://localhost:8084/user/logout/"+_ticket+"\" class=\"link-logout\">[退出]</a>";
var html = username + ",欢迎来到淘淘!<a href=\"javascript:logout()\" class=\"link-logout\">[退出]</a>";
$("#loginbar").html(html);
}
}
});
}
}
$(function(){
// 查看是否已经登录,如果已经登录查询登录信息
TT.checkLogin();
});
服务端 UserController getUserByToken
1、接收callback参数,取回调的js的方法名。
2、业务逻辑处理。
3、响应结果,拼接一个js语句。
方法1 依据请求参数是不是包含 callback 判断是否是jsonp请求
@RequestMapping(value="/user/token/{token}", method=RequestMethod.GET,
//指定返回响应数据的content-type
produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String getUserByToken(@PathVariable String token, String callback) {
TaotaoResult result = userService.getUserByToken(token);
//判断是否为jsonp请求
if (StringUtils.isNotBlank(callback)) {
return callback + "(" + JsonUtils.objectToJson(result) + ");";
}
return JsonUtils.objectToJson(result);
}
方法2
//jsonp的第二种方法,spring4.1以上版本使用
@RequestMapping(value="/user/token2/{token}", method=RequestMethod.GET)
@ResponseBody
public Object getUserByToken2(@PathVariable String token, String callback) {
TaotaoResult result = userService.getUserByToken(token);
//判断是否为jsonp请求
if (StringUtils.isNotBlank(callback)) {
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
//设置回调方法
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}
return result;
}
CookieUtils.java
cookieValue = URLEncoder.encode(cookieValue, “utf-8”);
Cookie cookie = new Cookie(cookieName, cookieValue);
cookie.setDomain(domainName);
cookie.setPath("/");
response.addCookie(cookie);
package com.taotao.common.utils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* Cookie 工具类
*
*/
public final class CookieUtils {
/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue) {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
* 设置Cookie的值 在指定时间内生效,但不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage) {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
* 设置Cookie的值 不设置生效时间,但编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, boolean isEncode) {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
}
/**
* 删除Cookie带cookie域名
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName) {
doSetCookie(request, response, cookieName, "", -1, false);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
try {
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = "." + domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}