1. 程式人生 > >SSO單點登入,簡單模擬

SSO單點登入,簡單模擬

SSO單點登入(以下全是個人理解,如果有誤,共同批評進步)

1.什麼是單點登入:

在不同的應用中,受保護的同一使用者,登入一次就可以訪問相關的其他系統應用。比如搜狐登入後,可以直接訪問部落格、郵箱等等,而不用再重新登入部落格系統、郵箱系統等等。方便了使用者的操作。

2.同域下單點登入實現:

單點登入流程和大概思路如下:


  我們用具體程式碼來實現以下操作:

我們有四個專案,app1、app2、ssofilter、ssoserver分別是web系統1、web系統2、攔截請求系統、sso認證中心。

app1系統結構如下:


就一個jsp檔案,用來獲取使用者資訊。app2系統的頁面也一樣。

我們先寫sso認證中心的程式碼,這裡只寫主要的程式碼:

登入的程式碼,登入成功後寫入cookie,將令牌和使用者資訊存入全域性資訊中

	/**
	 * get請求,表示沒有登入直接訪問。獲取路徑,返回登入頁面
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//獲取請求路徑
		String backurl=request.getParameter("backurl");
		request.setAttribute("backurl", backurl);
		request.getRequestDispatcher("index.jsp").forward(request, response);
	}
	
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//解決中文亂碼
		request.setCharacterEncoding("utf-8");
		String username=request.getParameter("username");
		String password=request.getParameter("password");
		String backurl=request.getParameter("backurl");
		try {
			User user=new User();
			//根據使用者名稱名稱查詢使用者
			user = UserUtil.getUserByname(username);
			if(null!=user&& password.equals(user.getPassword())){
				System.out.println("登入成功!");
				/*String[] uuid=UUID.randomUUID().toString().split("-");
				StringBuffer token=new StringBuffer();
				for (String string : uuid) {
					token.append(string);
				}*/
				//生成令牌,根據java生成uuid,將其中的-符號替換為空字串,然後轉成小寫字母
				String token=UUID.randomUUID().toString().replace("-", "").toLowerCase();
				//根據自定義的全域性工具類,將令牌作為key,將使用者資訊作為value存入Map中
				MapUtil.addToken(token, user);
				//建立一個cookie
				Cookie cookie=new Cookie("token", token);
				cookie.setHttpOnly(true);
				cookie.setPath("/");
				//將cookie添入響應中,返回客戶端
				response.addCookie(cookie);
				if(StringUtils.isNullOrEmpty(backurl)){
					//如果沒有獲取到請求路徑,表示使用者直接訪問login登入頁面,這裡可以將值設定為登入成功後的主頁
					backurl="success";
				}else{
					//將路徑解碼
					backurl=URLDecoder.decode(backurl,"utf-8");
				}
				//重新定向到返回路徑
				//response.sendRedirect(backurl);
				response.getWriter().println(backurl);
			}else{
				response.setContentType("text/html;utf-8");
				response.setCharacterEncoding("utf-8");
				System.out.println("使用者名稱或密碼不對!");
				response.getWriter().println("使用者名稱或密碼不對");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
SSO認證中心,驗證程式碼:
		//獲取請求令牌
		String token=request.getParameter("token");
		//根據令牌去請求對應的使用者資訊
		User user=MapUtil.getUserByToken(token);
		//判斷令牌是否有效
		if(null!=user){
			//有效,返回使用者資訊
			response.getWriter().println("username="+URLEncoder.encode(user.getUsername(),"utf-8")+";password="+user.getPassword());
		}else{
			//無效,返回空字串
			response.getWriter().println("");
		}
攔截請求系統程式碼:
		//將ServletRequest和ServletResponse轉為Http請求的
		HttpServletRequest httprequest=(HttpServletRequest)request;
		HttpServletResponse httpresponse=(HttpServletResponse)response;
		//獲取cookie陣列
		Cookie[] cookies=httprequest.getCookies();
		String token=null;
		if(null!=cookies && 0<cookies.length){
			for (Cookie cookie : cookies) {
				if(cookie.getName().equals("token")){
					token=cookie.getValue();
				}
			}
		}
		//獲取url路徑,?之前
		String url=httprequest.getRequestURI();
		//獲取?之後的路徑
		String queryurl=httprequest.getQueryString();
		if(null!=queryurl){
			url+="?"+queryurl;
		}
		if(null==token){
			//令牌為空,直接返回登入頁面,重新登入生成
			httpresponse.sendRedirect(SSOLOGINURL+"?backurl="+URLEncoder.encode(url,"utf-8"));
		}else{
			//請求SSO服務端,驗證令牌的有效性
			URL urlconn=new URL(SSOVAILDATE+"?token="+token);
			HttpURLConnection connection=(HttpURLConnection)urlconn.openConnection();
			connection.setRequestMethod("GET");  
			connection.setRequestProperty("Charset", "utf-8");
			connection.connect();
			InputStream input=connection.getInputStream();
			byte[] by=new byte[1024];
			input.read(by);
			String stringbuffer=new String(by);
			if(stringbuffer.length()>0){
				String[] userdata=stringbuffer.split(";");
				User user=new User();
				for (String string : userdata) {
					String[] string2=string.split("=");
						switch (string2[0]){
						case "username":
							user.setUsername(URLDecoder.decode(string2[1],"utf-8"));
							break;
						case "password":
							user.setPassword(string2[1]);
							break;
						}
					}
					request.setAttribute("user", user);
					filter.doFilter(httprequest, httpresponse);
				}else{
					//標示token無效,將請求定向到登入地址
					httpresponse.sendRedirect(SSOLOGINURL+"?backurl="+URLEncoder.encode(url,"utf-8"));
				}
			}
攔截請求系統打成jar包,加入web1和web2中使用,在web1和web2中配置攔截器
  <filter>
  	<filter-name>loginfilter</filter-name>
  	<filter-class>com.client.ssoFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>loginfilter</filter-name>
  	<url-pattern>*.jsp</url-pattern>
  </filter-mapping>

還有SSO認證中心的登入頁面:

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>   
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>登入</title>
<script type="text/javascript">
function submitData(){
	var httpRequest=createXMLHttpRequest();
	httpRequest.onreadystatechange=function(){
		if(httpRequest.readyState==4 && httpRequest.status==200){
			if("使用者名稱或密碼不對".trim()==(httpRequest.responseText).trim()){
				alert(httpRequest.responseText);
				return false;
			}else{
				location.href=httpRequest.responseText;
			}
		}
	};
	var postdata="username="+document.getElementById("username").value+"&backurl="+document.getElementById("backurl").value+"&password="+document.getElementById("password").value;
	/* var forms=new FormData();
	forms.append("username", document.getElementById("username").value);
	forms.append("backurl", document.getElementById("backurl").value);
	forms.append("password", document.getElementById("password").value); */
	httpRequest.open("post", document.getElementById("dataFrom").getAttribute("action"),true);
	httpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	httpRequest.send(postdata);
}
function createXMLHttpRequest(){
	var xmlHttp;
    if(window.ActiveXObject)
    {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    } 
    else if(window.XMLHttpRequest)
    {
        xmlHttp = new XMLHttpRequest();
    }
    return xmlHttp;
}
</script>
</head>
<body>
<form action="login" method="post" id="dataFrom">
	<table>
		<tr>
			<td><input type="hidden" name="backurl" id="backurl" value="${backurl}"/></td>
		</tr>
		<tr>
			<td><input type="text" name="username" id="username"/></td>
		</tr>
		<tr>
			<td><input type="password" name="password" id="password"/></td>
		</tr>
		<tr>
			<td><input type="button" value="登入" onclick="submitData()"/>     <input type="reset" value="重新輸入"/></td>
		</tr>
	</table>
</form>
</body>
</html>