天天看点

NC集成CAS统一认证+单点登录原理

原理及步骤:

1、浏览器中输入应用地址http://IP:port/inedx.jsp。

进入NC服务器拦截器1处理:如果URI是/index.jsp且ticket==null,且Assertion==null,则跳转到CAS认证页面。

2、CAS登录成功,跳转回业务系统/index.jsp页面。

进入NC服务器拦截器1处理:此时ticket!=null,流转到拦截器2进行ticket校验。

3、拦截器2校验未通过则终止;通过则设置Assertion(包含用户名等信息),并跳转到/index.jsp;jsessionid=XX。注意此时的URI是/index.jsp;jsessionid=XX,而不是/inedx.jsp。

4、拦截器1进行处理,此时ticket==null但Assertion!=null,进行ssokey注册和跳转到单点登录地址,CAS单点登录成功。

流程图:

NC集成CAS统一认证+单点登录原理

web.xml配置

<!--统一认证 start-->
	<!--用于实现单点退出-->
	<listener>
		<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
	</listener>
	<filter>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!--该过滤器负责用户的认证工作,判断用户是否登录(必须) start-->
	<filter> 
		<filter-name>CASAuthenticationFilter</filter-name> 
		<filter-class>nc.bs.cas.login.AuthenticationFilter</filter-class> 
		<init-param> 
			<!--注意casServerLoginUrl指服务器的地址 -->
			<param-name>casServerLoginUrl</param-name> 
			<param-value>http://XX:XX</param-value> 
		</init-param> 
		<init-param> 
			<!-- 当指定renew为true时,在请Cas Server时将带上参数“renew=true”,默认为false -->
			<param-name>renew</param-name> 
			<param-value>false</param-value> 
		</init-param> 
		<init-param> 
			<!-- 指定gateway为true时,在请求Cas Server时将带上参数“gateway=true”,默认为false。 -->
			<param-name>gateway</param-name> 
			<param-value>false</param-value> 
		</init-param> 
		<init-param> 
			 <!--而serverName指的是应用的地址 -->
			<param-name>serverName</param-name> 
			<param-value>http://XX:XX</param-value> 
		</init-param>
	</filter>
	<filter-mapping> 
		<filter-name>CASAuthenticationFilter</filter-name> 
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!--该过滤器负责用户的认证工作 end-->
	<!-- 该过滤器配置负责对Ticket的校验工作,对于client接收到的ticket进行验证(必须) start -->
	<!--这个过滤器可以从ticket取出CAS的session赋值到应用系统-->
	<filter> 
		<filter-name>CASValidationFilter</filter-name> 
		<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> 
		<init-param> 
			<!--注意casServerLoginUrl指服务器的地址 -->
			<param-name>casServerUrlPrefix</param-name> 
			<param-value>http://XX:XX</param-value> 
		</init-param> 
		<init-param>
		 	<!--而serverName指的是应用的地址 -->
			<param-name>serverName</param-name> 
			<param-value>http://XX:XX</param-value> 
		</init-param> 
		<init-param> 
			<param-name>useSession</param-name> 
			<param-value>true</param-value> 
		</init-param> 
		<init-param> 
			<param-name>redirectAfterValidation</param-name> 
			<param-value>true</param-value> 
		</init-param> 
	</filter> 
	<filter-mapping> 
		<filter-name>CASValidationFilter</filter-name> 
		<url-pattern>/*</url-pattern> 
	</filter-mapping> 
	<!-- 该过滤器配置负责对Ticket的校验工作 end -->

	<!--统一认证 end-->
           

拦截器1核心代码:

private boolean isExclude(HttpServletRequest request){  
    	boolean isInWhiteList = true;
    	String uri=request.getRequestURI();
    	// /index.jsp
    	// 1. /index.jsp 跳转到CAS认证页面 
    	// 2. /index.jsp 有ticket 认证页面第一次跳回系统时有ticket,让校验器处理
    	if("/index.jsp".equals(uri)){
    		isInWhiteList = false;
    	}
    	// /index.jsp;jsessionid=82547E908AAE29F76F5D598533818E6D.server
    	// 3. 校验器处理通过后重定向到系统,/index.jsp;jsessionid或者/;jsessionid
    	else if(uri!=null&&(uri.startsWith("/index.jsp;jsessionid")||uri.startsWith("/;jsessionid"))){
    		isInWhiteList = false;
    	}
        return isInWhiteList;
    }  

    @Override
	public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException
    {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;        

        if(isExclude(request)){
            filterChain.doFilter(request, response);
            return;
        }
        
        
        String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
        
        // 只有从认证页面跳回系统那一次请求有ticket
        if(CommonUtils.isNotBlank(ticket))
        {
        	log.error("######check ticket, uri="+request.getRequestURI()+", ticket="+ticket);
            filterChain.doFilter(request, response);
            return;
        }
    	
        // 存在session则返回,否则返回null
        HttpSession session = request.getSession(false);
        
        Assertion assertion = session == null ? null : (Assertion)session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);

        if(assertion != null)
        {
        	String user=InvocationInfoProxy.getInstance().getUserCode();
        	if(isNullStr(user)){
        		log.error("######register ssoKey, uri="+request.getRequestURI());
        		String loginName = assertion.getPrincipal().getName();
        		// 登录NC
        		String appServer=this.serverName;
        		if(appServer==null){
        			appServer=request.getScheme() + "://" + request.getLocalAddr()+":"+request.getLocalPort();
        		}else if(appServer.endsWith("/")){
        			appServer=appServer.substring(0, appServer.length()-1);
        		}
        		// 1.生成ssoKey
        		String randomKey = IDMaker.makeID(30);
        		// 拼接NCURL跳转URL ssoRegServlet?ssoKey=key&userCode=
        		String regUrl = appServer + "/service/ssoRegServlet?ssoKey=" + randomKey + "&userCode=" + loginName;
        		// 2.注册ssoKey
        		try {
        			registerConnect(regUrl);
        		} catch (Exception e) {
        			Logger.error("注册ssoKey时异常", e);
        		}
        		// 3.通过ssoKey登录
        		String newUrl=appServer + "/login.jsp?ssoKey=" + randomKey;
        		response.sendRedirect(newUrl);
        		return;
        	}
            filterChain.doFilter(request, response);
            return;
        }else{        
	        // 控制是否启用CAS统一认证,系统参数异常时不走CAS
	    	try {
	    		List<SysInitVO>list=(List<SysInitVO>) getDao().retrieveByClause(SysInitVO.class, "initcode ='" + SQLTransferMeaningUtil.tmsql(AuthenticationFilter.PARAM_CAS) + "'and pk_org='" + SQLTransferMeaningUtil.tmsql(IOrgConst.GLOBEORG) + "'");   		
	    		if(list!=null&&list.size()>0){
	    			UFBoolean cas=new UFBoolean(list.get(0).getValue());
		    		if(cas!=null&&cas.booleanValue()==false){
		    			filterChain.doFilter(request, response);
		                return;
		    		}
	    		}
			} catch (Exception e1) {
				Logger.error("获取系统参数错误", e1);
				filterChain.doFilter(request, response);
	            return;
			}
	    	
	    	log.error("######sendRedirect to cas, uri="+request.getRequestURI());
	    	
	        Logger.debug("no ticket and no assertion found");
	        
	        String serviceUrl = constructServiceUrl(request, response);
	        String modifiedServiceUrl;
	        if(gateway)
	        {
	        	Logger.debug("setting gateway attribute in session");
	            modifiedServiceUrl = gatewayStorage.storeGatewayInformation(request, serviceUrl);
	        } else
	        {
	            modifiedServiceUrl = serviceUrl;
	        }
	        
	        String urlToRedirectTo = CommonUtils.constructRedirectUrl(casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, renew, gateway);
	        
	        // 重定向
	        response.sendRedirect(urlToRedirectTo);
	        return;
        }
    }