`

SpringMVC杂记(十二) 自定义Interceptor从Active Directory得到域信息

 
阅读更多
一)最近项目中要求实现Web应用的SSO(Single Sign On),即对于已经登录到Windows Domain中的用户,不需要输入用户名、密码而直接使用当前登录的Domain用户信息进行验证,如果验证成功则进入,否则拒绝进入。
参考了一下其他朋友的博客,大致了解了一下NTLM协议。

二) 我设计一个Interceptor,通过这个Interceptor的请求的session就会自然被设置上域名和用户名。
<mvc:interceptor>
	<mvc:mapping path="/security/login"/>
	<bean class="ying.interceptor.ADUserInfoSetInterceptor">
		<property name="domain" value="*.*.com" />
		<property name="domainController" value="192.168.**" /> <!-- 域服务器ip -->
		<property name="requestPredicate">
			<bean class="ying.function.InDomainPredicate" />
		</property>
	</bean>
</mvc:interceptor>

当然,不一定要用interceptor,servlet-filter一样可以实现同样的功能。spring-mvc用习惯了,还是interceptor顺手一些。

其中被注入的requestPredicate,是一个谓词,用来过滤一些请求,不满足的谓词要求的request会被忽略掉。如下面这个谓词用来判断Client是否已经在公司的域里了
package ying.function;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;

public class InDomainPredicate implements Predicate {

	@Override
	public boolean evaluate(Object object) {
		if (! (object instanceof HttpServletRequest)) {
			return false;
		}
		
		HttpServletRequest request = (HttpServletRequest) object;
		String ip = request.getRemoteHost();
		
		if ("127.0.0.1".equals(ip) || "localhost".equals(ip)) {
			return false;
		}
		
		try {
			Process p = Runtime.getRuntime().exec(String.format("nbtstat -A %s", ip));
			InputStreamReader ir = new InputStreamReader(p.getInputStream());
			LineNumberReader input = new LineNumberReader(ir);
			String workstation = null;
			String domain = null;
			int k = 0;
			for (int i = 1; i < 20; i++) {
				String str = StringUtils.defaultIfEmpty((input.readLine()), "");
				if (str.indexOf("<00>") != -1) {
					if (StringUtils.isEmpty(workstation)) {
						workstation = str.substring(0, str.indexOf("<00>")).trim();
						k = i;
					}

					if (StringUtils.isEmpty(domain) && (k > 0 && (i == k + 1))) {
						domain = str.substring(0, str.indexOf("<00>")).trim();
					}
				}
			}
			return (workstation.startsWith("ZT") && workstation.contains("-") && "ZTGAME".equals(domain));
		} catch (IOException e) {
			return false;
		}
	}
}


package ying.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcifs.Config;
import jcifs.UniAddress;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.smb.SmbSession;
import jcifs.util.Base64;

import org.apache.commons.collections.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;


public class ADUserInfoSetInterceptor extends HandlerInterceptorAdapter implements InitializingBean {

	private static final Logger LOGGER = LoggerFactory.getLogger(ADUserInfoSetInterceptor.class);
	
	private String domainController = null;
	private String domain = null;
	private String useExtendedSecurity = "false";
	private String lmCompatibility = "0";
	private String cachePolicy = "1200";
	private String soTimeout = "1800000";
	private Predicate requestPredicate;
	private String domainAttributeName = "domainAttributeName";
	private String nameAttributeName = "nameAttributeName";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		
		if (requestPredicate != null && ! requestPredicate.evaluate(request)) {
			LOGGER.debug("requestPredicate.evaluate(request) == false");
			return true;
		}
		
		UniAddress dc = null;
		String msg = request.getHeader("Authorization");

		if (msg != null && msg.startsWith("NTLM ")) {
			byte[] src = Base64.decode(msg.substring(5));
			dc = UniAddress.getByName(domainController, true);
			byte[] challenge = SmbSession.getChallenge(dc);
			if (src[8] == 1) {
				Type1Message type1 = new Type1Message(src);
				Type2Message type2 = new Type2Message(type1, challenge, null);
				msg = Base64.encode(type2.toByteArray());
				if (response != null) {
					response.setHeader("WWW-Authenticate", "NTLM " + msg);
					response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
					response.setContentLength(0);
					response.flushBuffer();
					LOGGER.debug("C <--  S   401 Unauthorized WWW-Authenticate: NTLM <base64-encoded type-2-message>");
					return false;
				}
			} else if (src[8] == 3) {
				Type3Message type3 = new Type3Message(src);
				byte[] lmResponse = type3.getLMResponse();
				if (lmResponse == null)
					lmResponse = new byte[0];
				byte[] ntResponse = type3.getNTResponse();
				if (ntResponse == null)
					ntResponse = new byte[0];

				request.getSession(true).setAttribute(this.nameAttributeName, type3.getUser());
				request.getSession(true).setAttribute(this.domainAttributeName, type3.getDomain());
				
				LOGGER.debug("C  --> S   GET ... Authorization: NTLM <base64-encoded type-3-message>");
			}
		} else {
			response.setHeader("WWW-Authenticate", "NTLM");
			response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
			response.setContentLength(0);
			response.flushBuffer();
			LOGGER.debug("C <--  S   401 Unauthorized");
			return false;
		}

		return true;
	}
	
	private void init() {
		Config.setProperty("jcifs.smb.client.soTimeout", getSoTimeout());
		Config.setProperty("jcifs.netbios.cachePolicy", getCachePolicy());
		Config.setProperty("jcifs.smb.lmCompatibility", getLmCompatibility());
		Config.setProperty("jcifs.smb.client.useExtendedSecurity", getUseExtendedSecurity());
		Config.setProperty("jcifs.http.domainController", getDomainController());
		Config.setProperty("jcifs.smb.client.domain", getDomain());
	}
	
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(domain);
		Assert.notNull(domainController);

		init();
	}

	// ---------------------------------------------------------------------------------------------
	// getter and setter
	// 为了节约版面不写了
}


三) 完成登录功能,登录还是由安全框架负责,如spring-security或apache-shiro
本文不赘述!

四) 开源软件jcifs的maven坐标
<dependency>
	<groupId>jcifs</groupId>
	<artifactId>jcifs</artifactId>
	<version>1.3.17</version>
</dependency>
分享到:
评论
1 楼 3108493554 2016-03-18  
你好 ,有些问题想请教下,加下我qq310849354,你这上面的代码是ntlm验证成功就跳转到项目的主页,验证失败就跳到项目的登录页面是吗?

相关推荐

    springmvc-login-interceptor

    在这个名为"springmvc-login-interceptor"的项目中,我们关注的核心是Spring MVC的拦截器(Interceptor)功能,它允许开发者在请求被控制器处理之前或之后执行自定义逻辑。拦截器在实际应用中常用于权限验证、日志...

    springmvc+shiro自定义过滤器的实现代码

    SpringMVC+Shiro自定义过滤器的实现代码 itle"springmvc+shiro自定义过滤器的实现代码"所涉及的知识点如下: 1. SpringMVC拦截器 在SpringMVC中,拦截器(Interceptor)是一种特殊的Bean,它可以在请求处理之前、...

    SpringMVC中使用Interceptor拦截器

    在Spring MVC框架中,Interceptor(拦截器)是一个强大的工具,用于在请求被控制器处理之前或之后执行特定的逻辑。它们可以用来实现通用的功能,如权限验证、日志记录、性能统计等,避免在每个控制器方法中重复编写...

    自定义的springMVC

    - **拦截器(Interceptor)**:自定义拦截器可以实现全局的功能,比如权限验证、日志记录、性能统计等。通过实现HandlerInterceptor接口并将其注册到SpringMVC配置中,即可在请求处理前后执行自定义逻辑。 - **...

    SpringMVC中午+自定义转换器-2021-04-10.txt

    在进行web项目的开发时,如果时间让用户自己输入,该怎么处理,SpringMVC的转换器可以处理,但是需要自己定义,如何定义和搭配呢?

    SpringMVC杂记(五) JSR303数据验证

    在本篇《SpringMVC杂记(五) JSR303数据验证》中,我们将深入探讨SpringMVC框架如何集成并使用JSR303(JavaBeans Validation)来进行数据验证,这是一种强大的验证机制,可以确保应用程序接收到的数据是合法且符合...

    SpringMVC杂记(三) 向导型Controller的实现(基于SpringMVC 3.1.1)

    在本篇博文中,我们将深入探讨SpringMVC框架中的向导型Controller的实现,这是SpringMVC 3.1.1版本的一个重要特性。向导型Controller通常用于处理多步骤的用户交互流程,如购物车结账、用户注册等,它能够引导用户...

    SpringMvc自定义注解获取用户登陆信息.rar

    本示例聚焦于如何通过自定义注解来获取用户登录信息,以增强应用的安全性和用户体验。下面将详细阐述这个过程中的关键知识点。 1. **Spring MVC与Spring Boot简介** Spring MVC是Spring框架的一个模块,专门用于...

    Maven+SpringMVC+Mybatis自定义分页查询标签

    以Maven构建的聚合项目,使用SpringMVC+Mybatis框架,基于JSP Tag技术的分页标签,详细说明见: http://blog.csdn.net/autfish/article/details/52023143

    基于maven+springmvc+Ueditor,自定义上传图片路径,稳得不行

    单图片上传到本地,编辑框回显, 多图片上传 回显。springmvc整合完成项目 详情 https://blog.csdn.net/m0_37946870/article/details/79913803

    SpringMVC利用AOP实现自定义注解记录日志

    本文抛砖引玉,并没有详细的介绍更全面的内容,通过一个例子让初次使用的人能够快速入门,简单的介绍一下。 第一,注解: @Before – 目标

    SpringMVC自定义多视图

    例如,我们可以创建一个自定义的ViewResolver实现,重写`resolveViewName`方法,该方法接受视图名和本地化信息作为参数,然后根据视图名返回相应的视图实例。视图实例可以是任何形式的,如FreeMarker模板、Thymeleaf...

    springmvc(自定义拦截器的使用)

    自定义拦截器是扩展Spring MVC功能、添加自定义行为的一种常见方式。以下是对自定义拦截器实现步骤的详细解释: ### 1. 自定义拦截器类 #### 1.1 继承 `HandlerInterceptorAdapter` 你可以选择继承`...

    SSM笔记-SpringMVC的自定义拦截器

    在SpringMVC框架中,拦截器(Interceptor)是一种强大的机制,它可以用来在请求处理之前、之后或处理过程中执行额外的逻辑。自定义拦截器允许开发者根据业务需求进行更精细的控制,例如权限验证、日志记录、性能统计...

    SpringMVC PPT_springmvc_

    SpringMVC 的拦截器(Interceptor)允许在请求处理前后执行自定义逻辑,例如登录检查、日志记录、性能监控等。拦截器通过 HandlerInterceptor 接口实现。 九、数据绑定与验证 SpringMVC 提供了自动的数据绑定功能,...

    SpringMvc自定义拦截器(注解)代码实例

    在Spring MVC框架中,拦截器(Interceptor)是一个强大的工具,它允许我们在请求处理前后执行自定义逻辑,如权限检查、日志记录、性能监控等。本文将深入探讨如何使用注解来自定义Spring MVC的拦截器。 首先,...

    SpringMVC DispatcherServlet重写、自定义拦截器拦截器源码

    SpringMVC DispatcherServlet重写、自定义拦截器拦截器源码

    fengchao111-springmvc-interceptor-master.zip

    【标题】"fengchao111-springmvc-interceptor-master" 是一个关于Spring MVC拦截器(Interceptor)的开源项目。Spring MVC是Spring框架的一部分,专门用于构建Web应用程序,而拦截器则是在请求被控制器处理之前或...

    Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源

    本项目“Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源”正是针对这一需求提供的一种解决方案。下面将详细介绍这个项目中的关键技术点和实现原理。 首先,Spring框架是Java企业级应用开发的核心...

    springMvcRequestNotFindCapture:springMvc中的自定义捕获异常

    springMvc中的自定义捕获异常 必须在spring.xml文件中注释mvc:default-servlet-handler / 在DispatcherServlet中重写noHandlerFound方法,将重定向到sx.java上的/ demo / notFound路径和定义找不到请求。 方法

Global site tag (gtag.js) - Google Analytics