sso

1、Client1-创建客户端client1项目(java web项目)

对应pom.xml引入

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.2.3.RELEASE</version>
    </dependency>
  </dependencies>

2、Client1-新建LoginFilter文件,在这里doFilter方法中目前先直接放行

package com.sso.client1.filter;

import com.sso.client1.util.SSOClientUtil;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * @param
 * @Author wangbin
 * @description
 * @CreateDate 2019/6/21
 * @return
 */
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //直接放行
        chain.doFilter(request,response);
        return;
    }

    @Override
    public void destroy() {

    }
}

3、Client1-web.xml配置文件添加LoginFilter过滤器,拦截所有的请求

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>

  <display-name>Archetype Created Web Application</display-name>

  <filter>
    <filter-name>loginFilter</filter-name>
    <filter-class>com.sso.client1.filter.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>loginFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

4、Client1-添加一个index.jsp作为首页,直接放在webapp目录下(不要放WEB-INF目录)

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!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>客户端1</title>
</head>
<body>
<h3>客户端1的首页</h3>
<h3>客户端1端口号为8011</h3>
<h3>点击下面按钮退出登录</h3>
<a href="http://localhost:8080/logOut" >退出</a>
</body>
</html>

5、Client1-配置client1项目的tomcat,将http端口号设置为8011,为防止后面项目端口冲突,把JMX PORT端口号也改了

6、Client1-启动项目,到此能正常启动,访问localhost:8011/index.jsp能正常看到页面即可

7、Client1-完善过滤器的执行方法,具体的流程步骤都写在代码注释里面,里面用到SSOClientUtil工具类去判断token是否有效,后面会放上,这里不应该注重是如何判断的,而是应该注重这里需要去判断

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;
        //判断是否有局部会话
        HttpSession session = req.getSession();
        Object isLogin = session.getAttribute("isLogin");
        if(isLogin!=null && (Boolean) isLogin){
            //已经登录则直接放行访问页面
            chain.doFilter(request,response);
            return;
        }
        //未登录
        //判断是否有令牌(如果有则进行校验)
        String token = request.getParameter("token");
        if (token != null){
            //确认令牌token是否有效.
            Boolean verify = SSOClientUtil.verify(token, SSOClientUtil.getUrl(req,SSOClientUtil.CLIENTURL), session.getId());
            if (verify){
                //token有效,创建局部会话设置登录状态,并放行
                session.setAttribute("isLogin",true);
                chain.doFilter(request,response);
                return;
            }
        }
        //如果没有token,或者token无效
        //去认证中心验证是否有登录状态,可能其他系统已经登录了
        SSOClientUtil.redirectToSSOURL(req,resp);
    }

8、附上两个Util类

(1)SSOClientUtil.java

package com.sso.client1.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class SSOClientUtil {
	//统一认证中心地址(域名)
	public static final String SERVER_DOMAIN="http://localhost:8080";
	//统一认证中心检查是否已经登录的访问地址
	public static final String SERVER_CHECK_URL="/checkLogin?redirectUrl=";
	//统一认证中心的token认证地址
	public static final String SERVER_VERIFY_URL="/verify";
	//统一认证中心的登出地址
	public static final String SERVER_LOGOUT_URL="/logOut";
	//客户端的登出地址
	public static final String CLIENT_LOGOUT_URL="/logOut";
	//统一认证中心的token认证方法的token参数名
	public static final String TOKEN_NAME="token";
	//统一认证中心的token认证方法的登出地址参数名
	public static final String CLIENTURL="clientURL";
	//获取客户端重定向地址
	public static final String REDIRECT_URL="redirectURL";
	//统一认证中心的token认证方法的jsessionid参数名
	public static final String JSESSIONID="jsessionid";
	/**
	 * 获取客户端的登出地址 http://localhost:8011/logOut
	 * 获取客户端的重定向地址 http://localhost:8011/index.jsp
	 * 获取认证中心登出地址 http://localhost:8080/logOut
	 * @return
	 */
	public static String getUrl(HttpServletRequest request, String name){
		//获取请求协议,是http或者https
		String scheme = request.getScheme();
		//获取主机
		String host = request.getServerName();
		//获取端口号
		int port = request.getServerPort();
		//获取请求地址
		String servletPath = request.getServletPath();
		if(name != null && (CLIENTURL.equals(name)||name==CLIENTURL)){
			//返回客户端登出地址
			return scheme + "://" + host + ":" + port + CLIENT_LOGOUT_URL;
		}else if(name != null && (REDIRECT_URL.equals(name)||name==REDIRECT_URL)){
			//返回客户端重定向地址
			return scheme + "://"+host + ":"+port + servletPath;
		}else if(name != null && (SERVER_LOGOUT_URL.equals(name)||name==SERVER_LOGOUT_URL)){
			//返回认证中心登出地址
			String serverURL = SERVER_DOMAIN;
			String logOutURL = SERVER_LOGOUT_URL;
			return serverURL+logOutURL;
		}
		return "";
	}

	/**
	 * 根据request获取跳转到统一认证中心的地址
	 * http://localhost:8080/checkLogin?redirectUrl=http://localhost:8011/index.jsp
	 * 通过Response跳转到指定的地址
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public static void redirectToSSOURL(HttpServletRequest request,HttpServletResponse response) throws IOException{
		String serverURL = SERVER_DOMAIN;
		String checkURL = SERVER_CHECK_URL;
		String redirectUrl = getUrl(request,REDIRECT_URL);
		StringBuilder url = new StringBuilder(50);
		url.append(serverURL).append(checkURL).append(redirectUrl);
		response.sendRedirect(url.toString());
	}
	/**
	 * 验证token是否有效
	 * 如果有效把客户端的登出地址和jsessionid传递到统一认证中心,方便进行单点注销.
	 * @param token	
	 * @param clientURL
	 * @param jsessionid
	 * @return 
	 */
	public static Boolean verify(String token, String clientURL,String jsessionid) {
		String serverURL = SERVER_DOMAIN;
		String verifyURL = SERVER_VERIFY_URL;
		Map<String,String> params = new HashMap<String,String>();
		params.put(TOKEN_NAME, token);
		params.put(CLIENTURL, clientURL);
		params.put(JSESSIONID, jsessionid);
		try {
			//判断token是否有效,直接发送一个带上token参数的请求到认证中心
			//根据返回结果判断是否有效
			String responseContent = HttpUtil.sendHttpRequest(serverURL+verifyURL, params);
			if("true".endsWith(responseContent)){
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return false;
	}
}

(2)HttpUtil.java

package com.sso.client1.util;

import org.springframework.util.StreamUtils;

import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Map.Entry;

public class HttpUtil {
	/**
	 * 模拟浏览器的请求
	 * @param httpURL 发送请求的地址
	 * @param params  请求参数
	 * @return
	 * @throws Exception
	 */
	public static String sendHttpRequest(String httpURL,Map<String,String> params) throws Exception{
		//建立URL连接对象
		URL url = new URL(httpURL);
		//创建连接
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		//设置请求的方式(需要是大写的)
		conn.setRequestMethod("POST");
		//设置需要响应结果
		conn.setDoOutput(true);
		//判断是否有参数.
		if(params!=null&&params.size()>0){
			StringBuilder sb = new StringBuilder();
			for(Entry<String,String> entry:params.entrySet()){
				sb.append("&").append(entry.getKey()).append("=").append(entry.getValue());
			}
			//sb.substring(1)去除最前面的&
			conn.getOutputStream().write(sb.substring(1).toString().getBytes("utf-8"));
		}
		//发送请求到服务器
		conn.connect();
		//获取远程响应的内容.
		String responseContent = StreamUtils.copyToString(conn.getInputStream(),Charset.forName("utf-8"));
		conn.disconnect();
		return responseContent;
	}
	/**
	 * 模拟浏览器的请求
	 * @param httpURL 发送请求的地址
	 * @param jesssionId 会话Id
	 * @return
	 * @throws Exception
	 */
	public static void sendHttpRequest(String httpURL,String jesssionId) throws Exception{
		//建立URL连接对象
		URL url = new URL(httpURL);
		//创建连接
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		//设置请求的方式(需要是大写的)
		conn.setRequestMethod("POST");
		//设置需要响应结果
		conn.setDoOutput(true);
		conn.addRequestProperty("Cookie","JSESSIONID="+jesssionId);
		//发送请求到服务器
		conn.connect();
		conn.getInputStream();
		conn.disconnect();
	}
}

9、重启项目,访问localhost:8011/index.jsp,会检查到未登录,然后跳转到认证中心去检查是否有登录状态,这里因为还没写认证中心的项目,所有访问会出错,但是能看到已经跳转,并且带上了重定向地址

10、到此已经完成client1项目登录的代码(还有部分登出注销的逻辑,后面补上)

11、同样的方法创建server项目,项目结构如下

12、附上web.xml文件,以及pom.xml依赖部分、applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
<dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
    </dependency>

    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.2.3.RELEASE</version>
    </dependency>
  </dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
			http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
	<context:component-scan base-package="com.sso.server.controller"/>
	<mvc:annotation-driven/>
	<mvc:default-servlet-handler/>
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
</beans>

13、LoginController的checkLogin方法、login方法、verity方法

package com.sso.server.controller;

import com.sso.server.util.HttpUtil;
import com.sso.server.util.SSOServerUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.*;

@Controller
public class LoginController {

    /**
     * 检查是否有登录状态
     * @param redirectUrl
     * @return
     */
    @RequestMapping("checkLogin")
    public String checkLogin(String redirectUrl,Model model,HttpSession session){
        //判断是否有其他系统登录过(判断是否有全局会话)
        Object token = session.getAttribute("token");
        if(token!=null){
            //重定向到应用系统,并且带上令牌
            model.addAttribute("token",token);
            return "redirect:"+redirectUrl;
        }
        model.addAttribute("redirectUrl",redirectUrl);
        //跳转到登录页面
        return "forward:/login.jsp";
    }

    /**
     * 登录表单提交的方法
     * @param redirectUrl
     * @return
     */
    @RequestMapping("login")
    public String login(String redirectUrl, String username, String password, Model model, HttpSession session){
        if(username.equals("admin")&&password.equals("1")){
            //登录成功
            //1.创建令牌
            String token = UUID.randomUUID().toString();
            //2.把令牌放到全局会话中
            session.setAttribute("token",token);
            //3.放到map容器中(只要存在该容器中的token都是合法的)
            SSOServerUtil.map.put(token,new ArrayList<Map<String, String>>());
            //把令牌带回到客户端
            model.addAttribute("token",token);
            //3.跳转到客户端
            return "redirect:"+redirectUrl;
        }else{
            //请求转发到登录页面
            model.addAttribute("errorMsg","登录失败!");
            model.addAttribute("redirectUrl",redirectUrl);
            return "forward:/login.jsp";
        }
    }

    /**
     * 校验令牌是否合法
     * @param token
     * @return
     */
    @RequestMapping("verify")
    @ResponseBody
    public String verify(String token,String clientURL,String jsessionid){
        //判断token是否存在map容器中,如果存在则代表合法
        List<Map<String, String>> clients = SSOServerUtil.map.get(token);
        if (clients!=null){
            //注册系统,系统的相关信息保存到list中
            Map map = new HashMap();//一个应用系统的注销地址和当前登录用户的sessionid
            map.put("clientURL",clientURL);
            map.put("jsessionid",jsessionid);
            clients.add(map);
            return "true";
        }else{
            return "false";
        }
    }

    /**
     * 注销
     */
    @RequestMapping("logOut")
    public String logOut(HttpSession session, Model model) throws Exception {
        //获取令牌
        Object token = session.getAttribute("token");
        //注销全局会话
        session.invalidate();
        List<Map<String, String>> clients = SSOServerUtil.map.remove(token);
        for (Map<String, String> client : clients) {
            //取出每个系统,让他们注销指定的session
            //客户端注销的地址
            String clientURL = client.get("clientURL");
            //要注销的session
            String jsessionid = client.get("jsessionid");
            //通知每个应用系统注销局部会话(告诉他要注销哪个人的会话)
            HttpUtil.sendHttpRequest(clientURL,jsessionid);
        }
        model.addAttribute("errorMsg","退出登录!");
        model.addAttribute("redirectUrl","http://localhost:8080/logOut.jsp");
        return "forward:/logOut.jsp";
    }
}

14、还没写完

15、写篇博客真是不简单

16、源码丢了,改天再写一个?

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值