时间:2017年3月22日星期三
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学示例源码:无
个人学习源码:https://github.com/zccodere/s...
第一章:概述
1-1 课程介绍及SSO介绍
课程目标
认识并理解SSO及其应用,并能根据其实现原理自行实现SSO。
学习内容
1.SSO的介绍和应用体验(以新浪为例)
SSO:一次登录,处处穿梭
2.SSO的分类介绍的实现探讨
同域SSO:不同的应用位于同一个域名下面
跨域SSO:不同的应用位于不同的域名下面
3.各种SSO的具体实现介绍的代码示例
同域SSO图示
1-2 SSO核心技术分析
SSO的实现步骤和原理
以旅游是购买的通票为例:
SSO特点:
1.必须要登陆一次
2.票据与验票机制
实现SSO的步骤拆解
关键:存储票据(购票与存储),查验票据(是否有票与是否有效)
核心技术点实现原理:
比照旅游进行
第二章:同域SSO
2-1 同域SSO准备工作
教学示例流程图
个人学习流程图
项目搭建
说明:教学使用SSH搭建演示项目,我在学习时使用springboot搭建,源码可以在我的github上查看、下载、运行等。因为本节主要是讲SSO单点登录,并不是将项目搭建,所以搭建过程省略。
项目命名为:myssosamedomain
2-2 编写统一登录接口
编写校验工具类
package com.myimooc.sso.web.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
* 登录校验工具类
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
public class LoginCheck {
/** 测试用户名 */
public static final String USERNAME="user";
/** 测试密码*/
public static final String PASSWORD="123";
/** Cookie键 */
public static final String COOKIE_NAME = "ssocookie";
/** Cookie值*/
public static final String COOKIE_VALUE = "sso";
/**
* 登录用户名和密码校验
* @param username 用户名
* @param password 密码
* @return true用户名和密码正确;false用户名或密码错误
*/
public static boolean checkLogin(String username,String password){
if(USERNAME.equalsIgnoreCase(username) &&
PASSWORD.equalsIgnoreCase(password)){
return true;
}
return false;
}
/**
* 校验Cookie
* @param request
* @return true正确;false错误
*/
public static boolean checkCookie(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
if( cookies == null){
return false;
}
for (Cookie cookie : cookies) {
if(COOKIE_NAME.equals(cookie.getName()) &&
COOKIE_VALUE.equals(cookie.getValue())){
return true;
}
}
return false;
}
}
编写校验控制器
package com.myimooc.sso.web.controller;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.web.util.LoginCheck;
/**
* SSO登录控制器
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
@Controller
@RequestMapping("/sso")
public class LoginController {
/**
* 处理用户登录请求
* @param username 用户名
* @param password 密码
* @param gotoUrl 登录成功后请求路径
* @param response
* @return
*/
@PostMapping("/doLogin")
public ModelAndView doLogin(String username,String password,
String gotoUrl,HttpServletResponse response){
ModelAndView mv = new ModelAndView("login_fail");
// 校验用户名和密码
boolean ok = LoginCheck.checkLogin(username, password);
// 判断是否登录成功
if(ok){
Cookie cookie = new Cookie(LoginCheck.COOKIE_NAME,LoginCheck.COOKIE_VALUE);
// 顶级域名下,所有应用都是可见的
cookie.setPath("/");
// 添加Cookie
response.addCookie(cookie);
mv.setViewName("redirect:"+gotoUrl);
}
return mv;
}
/**
* 跳转到登录页面
* @return
*/
@GetMapping("/login")
public ModelAndView login(){
return new ModelAndView("login");
}
}
编写登录页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<center>
<h1>请登录</h1>
<form action="/sso/doLogin" method="post">
<input type="hidden" name="gotoUrl" value="${gotoUrl}"/>
<span>用户名:</span><input type="text" name="username"/>
<span>密 码:</span><input type="password" name="password"/>
<input type="submit">
</form>
</center>
</body>
</html>
编写登录失败页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录失败</title>
</head>
<body>
<center>
<h1>登录失败</h1>
<a href="[@common.ctx/]/sso/login">重新登录</a>
</center>
</body>
</html>
2-3 编写登录页面demo1和demo2控制器
demo1控制器
package com.myimooc.sso.demo1;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.web.util.LoginCheck;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
public class DemoOneController {
@RequestMapping("/demo1")
public ModelAndView main(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
if (LoginCheck.checkCookie(request)) {
mv.setViewName("demo1");
return mv;
}
mv.addObject("gotoUrl", "/demo1");
mv.setViewName("login");
return mv;
}
}
demo2控制器
package com.myimooc.sso.demo2;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.web.util.LoginCheck;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
public class DemoTwoController {
@RequestMapping("/demo2")
public ModelAndView main(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
if (LoginCheck.checkCookie(request)) {
mv.setViewName("demo2");
return mv;
}
mv.addObject("gotoUrl", "/demo2");
mv.setViewName("login");
return mv;
}
}
2-4 编写DEMO1和DEMO2的主页
demo1页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>欢迎访问demo1</title>
</head>
<body>
<h1>这是demo1的主页</h1>
</body>
</html>
demo2页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>欢迎访问demo2</title>
</head>
<body>
<h1>这是demo2的主页</h1>
</body>
</html>
2-5 同域SSO最终效果演示
访问demo1页面需要登录
访问demo2页面需要登录
在demo1页面处登录
登录成功,可以访问demo1页面
再次访问demo2时,不需要登录即可访问
第三章:同父域SSO
3-1 准备工作
教学示例流程图
个人学习流程图
通过修改host文件,来模拟实现父子域名。
文件路径:C:\Windows\System32\drivers\etc\hosts
修改如下
说明:正常情况下,应该分别建立三个项目,对应demo1.x.com、demo2.x.com、check.x.com。但为了演示讲解方便,我们通过项目里面对应的包名来区分是哪个项目。
项目搭建
项目命名为:myssosamefather
3-2 统一登录接口
编写消息响应类
package com.myimooc.sso.util;
import java.io.Serializable;
/**
* 消息响应对象
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
public class RespMessage implements Serializable{
private static final long serialVersionUID = 1L;
/** 响应编号 */
private String respCode;
/** 响应消息 */
private String respMsg;
public String getRespCode() {
return respCode;
}
public void setRespCode(String respCode) {
this.respCode = respCode;
}
public String getRespMsg() {
return respMsg;
}
public void setRespMsg(String respMsg) {
this.respMsg = respMsg;
}
}
编写登录校验工具类
package com.myimooc.sso.util;
/**
* 登录校验工具类
*
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
public class LoginCheck {
/** 测试用户名 */
public static final String USERNAME = "user";
/** 测试密码 */
public static final String PASSWORD = "123";
/** Cookie键 */
public static final String COOKIE_NAME = "ssocookie";
/** Cookie值 */
public static final String COOKIE_VALUE = "sso";
/**
* 登录用户名和密码校验
*
* @param username
* 用户名
* @param password
* 密码
* @return true已登录;false未登录
*/
public static boolean checkLogin(String username, String password) {
if (USERNAME.equalsIgnoreCase(username) && PASSWORD.equalsIgnoreCase(password)) {
return true;
}
return false;
}
/**
* 校验Cookie
* @param cookieName
* @param cookieValue
* @return
*/
public static boolean checkCookie(String cookieName,String cookieValue) {
if (cookieName == null || cookieName=="") {
return false;
}
if (cookieValue == null || cookieValue=="") {
return false;
}
if (COOKIE_NAME.equals(cookieName) && COOKIE_VALUE.equals(cookieValue)) {
return true;
}
return false;
}
}
编写http请求工具类
package com.myimooc.sso.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import com.alibaba.fastjson.JSONObject;
/**
* http工具类
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
public class HttpUtils {
/**
* 向指定url路径发起get请求,校验cookie
* @param url 路径
* @param cookieName
* @param cookieValue
* @return
*/
public static RespMessage doGet(String url,String cookieName,String cookieValue){
RespMessage respMessage = new RespMessage();
HttpURLConnection httpURLConnection = null;
URL targetUrl = null;
try{
targetUrl = new URL(url+"?cookieName="+cookieName+"&cookieValue="+cookieValue);
httpURLConnection = (HttpURLConnection) targetUrl.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.connect();
InputStream in = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
StringBuffer sb = new StringBuffer();
String temp = null;
while((temp=br.readLine())!=null){
sb.append(temp);
}
br.close();
isr.close();
in.close();
JSONObject resultJson = JSONObject.parseObject(sb.toString());
respMessage.setRespCode(resultJson.getString("respCode"));
respMessage.setRespMsg(resultJson.getString("respMsg"));
return respMessage;
}catch (Exception e) {
respMessage.setRespCode("500");
respMessage.setRespMsg("Cookie校验请求失败");
return respMessage;
}finally {
if(httpURLConnection !=null){
httpURLConnection.disconnect();
}
}
}
}
check.x.com:编写认证中心控制器
package com.myimooc.sso.check.x.com;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.util.LoginCheck;
import com.myimooc.sso.util.RespMessage;
/**
* SSO登录控制器
*
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
@Controller
@RequestMapping("/sso")
public class LoginController {
/**
* 处理用户登录请求
*
* @param username
* 用户名
* @param password
* 密码
* @param gotoUrl
* 登录成功后请求路径
* @param response
* @return
*/
@PostMapping("/doLogin")
public ModelAndView doLogin(String username, String password, String gotoUrl, HttpServletResponse response) {
ModelAndView mv = new ModelAndView("login_fail");
// 校验用户名和密码
boolean ok = LoginCheck.checkLogin(username, password);
// 判断是否登录成功
if (ok) {
Cookie cookie = new Cookie(LoginCheck.COOKIE_NAME, LoginCheck.COOKIE_VALUE);
// 设置在父域下面
cookie.setDomain("x.com");
// 顶级域名下,所有应用都是可见的
cookie.setPath("/");
// 添加Cookie
response.addCookie(cookie);
mv.setViewName("redirect:" + gotoUrl);
}
return mv;
}
/**
* 校验cookie
* @param cookieName
* @param cookieValue
* @param response
* @return
*/
@GetMapping("/checkCookie")
@ResponseBody
public RespMessage checkCookie(String cookieName,String cookieValue,HttpServletResponse response){
RespMessage result = new RespMessage();
result.setRespCode("500");
result.setRespMsg("CookieName或CookieValue无效");
boolean isOk = LoginCheck.checkCookie(cookieName, cookieValue);
if(isOk){
result.setRespCode("200");
result.setRespMsg("Cookie有效");
}
return result;
}
/**
* 跳转到登录页面
*
* @return
*/
@GetMapping("/login")
public ModelAndView login() {
return new ModelAndView("login");
}
}
3-3 编写demo1和demo2项目的控制层
demo1.x.com:编写demo1项目控制器
package com.myimooc.sso.demo1.x.com;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
public class DemoOneController {
@RequestMapping("/demo1")
public ModelAndView main(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
//校验cookie是否为空
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
//校验cookie是否存在
for(Cookie cookie : cookies){
if("ssocookie".equals(cookie.getName())){
//向校验服务器发送校验请求
String url = "http://check.x.com:8080/sso/checkCookie";
RespMessage respMessage = HttpUtils.doGet(url, cookie.getName(), cookie.getValue());
if("200".equals(respMessage.getRespCode())){
mv.setViewName("demo1");
return mv;
}
}
}
}
mv.addObject("gotoUrl", "http://demo1.x.com:8080/demo1");
mv.setViewName("login");
return mv;
}
}
demo2.x.com:编写demo2项目控制器
package com.myimooc.sso.demo2.x.com;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
public class DemoTwoController {
@RequestMapping("/demo2")
public ModelAndView main(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
//校验cookie是否为空
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
//校验cookie是否存在
for(Cookie cookie : cookies){
if("ssocookie".equals(cookie.getName())){
//向校验服务器发送校验请求
String url = "http://check.x.com:8080/sso/checkCookie";
RespMessage respMessage = HttpUtils.doGet(url, cookie.getName(), cookie.getValue());
if("200".equals(respMessage.getRespCode())){
mv.setViewName("demo2");
return mv;
}
}
}
}
mv.addObject("gotoUrl", "http://demo2.x.com:8080/demo2");
mv.setViewName("login");
return mv;
}
}
3-4 编写登录页
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<center>
<h1>请登录</h1>
<form action="http://check.x.com:8080/sso/doLogin" method="post">
<input type="hidden" name="gotoUrl" value="${gotoUrl}"/>
<span>用户名:</span><input type="text" name="username"/>
<span>密 码:</span><input type="password" name="password"/>
<input type="submit">
</form>
</center>
</body>
</html>
3-5 同父域情况下的SSO效果演示
请注意观察浏览器URL地址
访问demo1.x.com:需要登录
访问demo2.x.com:需要登录
在demo1.x.com处登录成功后,跳转至demo1页面
再访问demo2.x.com的demo2页面时,不再需要登录了
第四章:跨域SSO的实现
4-1 准备工作
教学示例流程图
个人学习流程图
通过修改host文件,来模拟实现跨域应用。
文件路径:C:\Windows\System32\drivers\etc\hosts
修改如下
说明:正常情况下,应该分别建立三个项目,对应www.a.com、www.b.com、www.x.com。但为了演示讲解方便,我们通过项目里面对应的包名来区分是哪个项目。
项目搭建
项目命名为:myssocrossdomain
完成后的目录结构如下
4-2 编写统一登录接口
编写消息响应类
package com.myimooc.sso.util;
import java.io.Serializable;
import java.util.Map;
/**
* 消息响应对象
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
public class RespMessage implements Serializable{
private static final long serialVersionUID = 1L;
/** 响应编号 */
private String respCode;
/** 响应消息 */
private String respMsg;
/** 响应数据 */
private Map<String,Object> respArgs;
public String getRespCode() {
return respCode;
}
public void setRespCode(String respCode) {
this.respCode = respCode;
}
public String getRespMsg() {
return respMsg;
}
public void setRespMsg(String respMsg) {
this.respMsg = respMsg;
}
public Map<String, Object> getRespArgs() {
return respArgs;
}
public void setRespArgs(Map<String, Object> respArgs) {
this.respArgs = respArgs;
}
@Override
public String toString() {
return "RespMessage [respCode=" + respCode + ", respMsg=" + respMsg + ", respArgs=" + respArgs + "]";
}
}
编写http请求工具类
package com.myimooc.sso.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
/**
* Http请求工具类
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
public class HttpUtils {
/**
* 向指定url路径发起get请求
* @param url 请求路径
* @param param 请求参数
* @return
*/
public static RespMessage doGet(String url,Map<String,String> param){
RespMessage respMessage = new RespMessage();
HttpURLConnection httpURLConnection = null;
URL targetUrl = null;
try{
// 拼装请求参数
StringBuffer targetUrlStr = new StringBuffer(url).append("?");
for(Map.Entry<String, String> entry : param.entrySet()){
targetUrlStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
url = targetUrlStr.substring(0,targetUrlStr.length()-1);
targetUrl = new URL(url);
httpURLConnection = (HttpURLConnection) targetUrl.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.connect();
InputStream in = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
StringBuffer sb = new StringBuffer();
String temp = null;
while((temp=br.readLine())!=null){
sb.append(temp);
}
br.close();
isr.close();
in.close();
JSONObject resultJson = JSONObject.parseObject(sb.toString());
respMessage.setRespCode(resultJson.getString("respCode"));
respMessage.setRespMsg(resultJson.getString("respMsg"));
JSONObject resultJsonMap = JSONObject.parseObject(resultJson.getString("respArgs"));
respMessage.setRespArgs(resultJsonMap);
return respMessage;
}catch (Exception e) {
respMessage.setRespCode("500");
respMessage.setRespMsg("请求发起失败");
return respMessage;
}finally {
if(httpURLConnection !=null){
httpURLConnection.disconnect();
}
}
}
}
www.x.com:编写认证中心校验工具类
package com.myimooc.sso.www.x.com;
/**
* 登录校验工具类
*
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
public class LoginCheck {
/** 测试用户名 */
public static final String USERNAME = "user";
/** 测试密码 */
public static final String PASSWORD = "123";
/** Cookie键 */
public static final String COOKIE_NAME = "ssocookie";
/** Cookie值 */
public static final String COOKIE_VALUE = "sso";
/**
* 登录用户名和密码校验
*
* @param username
* 用户名
* @param password
* 密码
* @return true已登录;false未登录
*/
public static boolean checkLogin(String username, String password) {
if (USERNAME.equalsIgnoreCase(username) && PASSWORD.equalsIgnoreCase(password)) {
return true;
}
return false;
}
/**
* 校验Cookie
* @param cookieName
* @param cookieValue
* @return
*/
public static boolean checkCookie(String cookieName,String cookieValue) {
if (cookieName == null || cookieName=="") {
return false;
}
if (cookieValue == null || cookieValue=="") {
return false;
}
if (COOKIE_NAME.equals(cookieName) && COOKIE_VALUE.equals(cookieValue)) {
return true;
}
return false;
}
}
www.x.com:编写认证中心控制器
package com.myimooc.sso.www.x.com;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.myimooc.sso.util.RespMessage;
/**
* SSO登录控制器
*
* @author ZhangCheng
* @date 2017-03-22
* @version V1.0
*/
@Controller
@RequestMapping("/sso")
public class LoginController {
/**
* 校验用户信息
* @param param
* @return
*/
@GetMapping("/doLogin")
@ResponseBody
public RespMessage doLogin(String username,String password) {
RespMessage result = new RespMessage();
result.setRespCode("500");
result.setRespMsg("用户名或密码错误");
// 校验用户名和密码
boolean ok = LoginCheck.checkLogin(username,password);
// 判断是否登录成功
if (ok) {
result.setRespCode("200");
result.setRespMsg("用户名和密码正确");
List<Map<String,String>> targetCookies = new ArrayList<Map<String,String>>();
// 向www.a.com服务器发送增加cookie
Map<String,String> targetCookiea = new HashMap<String,String>();
String urla = "http://www.a.com/a/addCookie";
targetCookiea.put("targetUrl", urla);
targetCookiea.put("cookieName", LoginCheck.COOKIE_NAME);
targetCookiea.put("cookieValue", LoginCheck.COOKIE_VALUE);
// 向www.b.com服务器发送增加cookie
Map<String,String> targetCookieb = new HashMap<String,String>();
String urlb = "http://www.b.com/b/addCookie";
targetCookieb.put("targetUrl", urlb);
targetCookieb.put("cookieName", LoginCheck.COOKIE_NAME);
targetCookieb.put("cookieValue", LoginCheck.COOKIE_VALUE);
targetCookies.add(targetCookiea);
targetCookies.add(targetCookieb);
Map<String,Object> args = new HashMap<String,Object>();
args.put("targetCookies", targetCookies);
result.setRespArgs(args);
}
return result;
}
/**
* 校验cookie
* @param cookieName
* @param cookieValue
* @param response
* @return
*/
@GetMapping("/checkCookie")
@ResponseBody
public RespMessage checkCookie(String cookieName,String cookieValue){
RespMessage result = new RespMessage();
result.setRespCode("500");
result.setRespMsg("CookieName或CookieValue无效");
boolean isOk = LoginCheck.checkCookie(cookieName, cookieValue);
if(isOk){
result.setRespCode("200");
result.setRespMsg("Cookie有效");
}
return result;
}
}
www.x.com:编写登录页
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
<script type="text/javascript" src="[@common.ctx /]/static/plugin/jquery/jquery.min.js"></script>
<script type="text/javascript" src="[@common.ctx /]/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript" src="[@common.ctx /]/static/script/login.js"></script>
</head>
<body>
<center>
<h1>请登录</h1>
<form>
<input type="hidden" id="ctx" value="${contextPath}" />
<input id="gotoUrl_input" type="hidden" name="gotoUrl" value="${gotoUrl}"/>
<span>用户名:</span><input id="username_input" type="text" name="username"/>
<span>密 码:</span><input id="password_input" type="password" name="password"/>
<input id="login_button" type="button" value="登录">
</form>
</center>
</body>
</html>
www.x.com:编写login.js
/**
* 登录js
*/
$(function(){
var ctx = $("#ctx").val();
$("#login_button").click(function(){
login();
});
});
function login(){
// 获取登录信息
var username=$("#username_input").val();
var password=$("#password_input").val();
var path=$("#path_input").val();
var gotoUrl=$("#gotoUrl_input").val();
var requesturl="/a/doLogin";
$.ajax({
type:"POST",
async:false,//发送同步请求
url:requesturl,
data:"username="+username+"&password="+password,
success:function(result){
// 登录失败
if(result.respCode != 200 ){
alert(result.respMsg);
return;
}
// 登录成功
var targetCookies = result.respArgs.targetCookies;
// 向服务器发出添加cookie请求
$.each(targetCookies,function(i,targetCookie){
var targetUrl = targetCookie.targetUrl;
var cookieName = targetCookie.cookieName;
var cookieValue = targetCookie.cookieValue;
creat(targetUrl,cookieName,cookieValue);
});
}
});
// 跳转到目标页
window.location.href=gotoUrl;
}
/** js利用iframe实现跨域添加cookie */
function creat(targetUrl,cookieName,cookieValue){
var iframe = document.createElement('iframe');
var targetSrc = targetUrl+"?"+"cookieName="+cookieName+"&cookieValue="+cookieValue;
iframe.src=targetSrc;
document.body.appendChild(iframe);
}
4-3 编写登录校验接口
www.a.com:编写控制器
package com.myimooc.sso.www.a.com;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.util.RespMessage;
import com.myimooc.sso.util.HttpUtils;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
@RequestMapping("/a")
public class DemoOneController {
/**
* 跳转到demo1的主页
* @param request
* @return
*/
@RequestMapping("/demo1")
public ModelAndView demo1(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
//校验cookie是否为空
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
//校验cookie是否存在
for(Cookie cookie : cookies){
if("ssocookie".equals(cookie.getName())){
// 封装请求参数
Map<String,String> param = new HashMap<String,String>();
param.put("cookieName", cookie.getName());
param.put("cookieValue", cookie.getValue());
// 向校验服务器发送校验请求
String url = "http://www.x.com/sso/checkCookie";
RespMessage respMessage = HttpUtils.doGet(url, param);
// 校验通过
if("200".equals(respMessage.getRespCode())){
mv.setViewName("demo1");
return mv;
}
}
}
}
// 登录失败重新登录
String path = request.getContextPath();
mv.addObject("contextPath",path);
mv.addObject("path","a");
mv.addObject("gotoUrl", "http://www.a.com/a/demo1");
mv.setViewName("login");
return mv;
}
/**
* 用户登录
* @param param
* @return
*/
@PostMapping(value="/doLogin")
@ResponseBody
public RespMessage doLogin(@RequestParam Map<String,String> param){
// 向校验服务器发送校验请求
String url = "http://www.x.com/sso/doLogin";
RespMessage respMessage = HttpUtils.doGet(url, param);
System.out.println("SSO服务器响应消息:"+respMessage);
return respMessage;
}
/**
* 想当前域添加cookie
* @param cookieName
* @param cookieValue
* @param response
*/
@RequestMapping(value="/addCookie")
public void addCookie(String cookieName,String cookieValue,HttpServletResponse response){
Cookie cookie = new Cookie(cookieName,cookieValue);
cookie.setPath("/");
response.addCookie(cookie);
}
}
www.b.com:编写控制器
package com.myimooc.sso.www.b.com;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.myimooc.sso.util.HttpUtils;
import com.myimooc.sso.util.RespMessage;
/**
*
* @author ZhangCheng
* @date 2017-04-02
* @version V1.0
*/
@Controller
@RequestMapping("/b")
public class DemoTwoController {
@RequestMapping("/demo2")
public ModelAndView main(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
//校验cookie是否为空
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
//校验cookie是否存在
for(Cookie cookie : cookies){
if("ssocookie".equals(cookie.getName())){
// 封装请求参数
Map<String,String> param = new HashMap<String,String>();
param.put("cookieName", cookie.getName());
param.put("cookieValue", cookie.getValue());
// 向校验服务器发送校验请求
String url = "http://www.x.com/sso/checkCookie";
RespMessage respMessage = HttpUtils.doGet(url, param);
// 校验通过
if("200".equals(respMessage.getRespCode())){
mv.setViewName("demo2");
return mv;
}
}
}
}
// 登录失败重新登录
mv.addObject("contextPath",request.getContextPath());
mv.addObject("path","b");
mv.addObject("gotoUrl", "http://www.b.com/b/demo2");
mv.setViewName("login");
return mv;
}
/**
* 用户登录
* @param param
* @return
*/
@PostMapping(value="/doLogin")
@ResponseBody
public RespMessage doLogin(@RequestParam Map<String,String> param){
// 向校验服务器发送校验请求
String url = "http://www.x.com/sso/doLogin";
RespMessage respMessage = HttpUtils.doGet(url, param);
System.out.println("SSO服务器响应消息:"+respMessage);
return respMessage;
}
/**
* 向当前域添加cookie
* @param cookieName
* @param cookieValue
* @param response
*/
@RequestMapping(value="/addCookie")
public void addCookie(String cookieName,String cookieValue,HttpServletResponse response){
Cookie cookie = new Cookie(cookieName,cookieValue);
cookie.setPath("/");
response.addCookie(cookie);
}
}
4-4 跨域SSO效果演示
注意观察浏览器URL地址
访问www.a.com的a项目需要登录
访问www.b.com的b项目需要登录
在www.a.com域登录
登录成功
www.b.com域即可直接访问,免登陆
第五章:课程总结
5-1 课程总结
1.核心是COOKIE,需要注意设置的域、位置和安全性
注意COOKIE的加密
2.应用群的安全性问题:木桶效应
即应用群的安全性受限于某个安全性最低的应用
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。