java web 项目如何获取客户端登录帐号信息(用于SSO或其他)

前言

在java 中可以通过System 获取操作系统的相关信息。

类似:

String sys_user_name = System.getProperty("user.name");
或者

Map map = System.getenv();
String username = (String)map.get("USERNAME");
String compname = (String)map.get("COMPUTERNAME");
String userdomain =(String)map.get("USERDOMAIN");

但是这种方式获取的是服务端机器的信息。

举例来说,如果你使用的是Tomcat 的服务器, 那么, 以上方法获取的是Tomcat 所在机器的信息。

有的时候, 我们需要获取Web 客户端的一些信息, 用途可能是:

1. 记录Web项目的使用日志(哪个帐号在哪台机器上使用了哪个页面?)

2. 实现自动登录。

对于以上的需求1, 可能会说, 如果使用登录页面的帐号密码登录的话不就可以知道是谁登录了吗? 但并不是所有项目都是使用Form验证的方式, 可能是域帐号登录, 也可能是完全不需要登录的web 项目。


使用applet 获取

既然System 获取的是代码所在机器的信息,那么使用 applet 的方式不就可以了。

applet 获取客户端信息,再传递给服务端。

这种方式虽然可行, 但是客户端的浏览器安全的设置, JRE的安装与版本这些都制约着这种方式的有效性。

为了这样一个比较小的功能, 使用applet 显得太重了。


request.getRemoteUser

JSP的request 对象中有以下方法:

 out.println("remote address  "+request.getRemoteAddr());
 out.println("remote host  "+request.getRemoteHost());
 out.println("remote port "+request.getRemotePort());
 out.println("remote user  "+request.getRemoteUser());
以上看上去可以了。 但是 request.getRemoteUser 在有的时候返回的是null.

看一下getRemoteUser API的说明:

Returns the login of the user making this request, if the user has been authenticated, or null if the user has not been authenticated. Whether the user name is sent with each subsequent request depends on the browser and type of authentication. Same as the value of the CGI variable REMOTE_USER.

如果user 没有被认证的话, 返回的是null.又和http 的验证扯上了关系。


插入一段关于basic 认证的定义:

在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务 器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中, 如当用户名为anjuta,密码为:123456时,客户端将用户名和密码用“:”合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据 时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用 户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。


不管以上一段是否看懂, 认证的目的就是让符合权限的人才能访问对应的页面和资源。在Tomcat 下可以如下方式来实现Basic 认证。

1. 在tomcat 的conf 目录下,修改 tomcat-users.xml 文件,增加以下内容到 tomcat-users标签中

2.  在 tomcat 的webapps 目录下新建 auth 这样一个测试目录。

3.  在目录auth下, 新建chapter和WEB-INF两个目录。

4. 在 chapter下添加index.jsp,内容如下:



  
  
  
  
  
  
    This is Authenticate Test.
    <%
      out.println("remote user  "+request.getRemoteUser());
    %>
  

5. 在WEB-INF下新加web.xml内容如下:



   
        Authenticate Web Programming
   
   
        
        
            Restricted Area
          
          /chapter/*
        
        
          tomcat
          reader
        
   
   
       BASIC
       Authenticate yourself
       

好了, 启动tomcat ,访问 http://localhost:8080/auth/chapter/, 会出现:



要求输入用户名, 密码。

不输入,直接取消的话, 会出现 HTTP Status 401 错误。

正确输入 oscar999/oscar999 之后, 页面正常了。 而且request.getRemoteUser() 也抓到值了。


使用域帐号

以上是使用Basic 的方式进行认证, 哪些用户哪些权限需要配置在Tomcat 中。

但是有时候,希望直接通过域帐号进行验证。 也就是NTLM认证。

NTLM
默认设置(NTLM 协议)基于一种“提问 - 答复”机制来进行客户端验证。NTLM是NT LAN Manager的缩写,这也说明了协议的来源。NTLM 是 Windows NT 早期版本的标准安全协议,Windows 2000 支持 NTLM 是为了保持向后兼容。Windows 2000内置三种基本安全协议之一。


以上方式就可以获得登录域帐号等相关信息, 不过同样, 也会出现输入用户和密码的功能。

IE 和 Chrome 不会跳出输入用户名,密码窗口

<%
	String auth = request.getHeader("Authorization");
	if (auth == null) {
		response.setStatus(response.SC_UNAUTHORIZED);
		response.setHeader("WWW-Authenticate", "NTLM");
		response.flushBuffer();
		return;
	}
	if (auth.startsWith("NTLM ")) {
		byte[] msg = new sun.misc.BASE64Decoder().decodeBuffer(auth.substring(5));
		int off = 0, length, offset;
		if (msg[8] == 1) {
			byte z = 0;
			byte[] msg1 = { (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M', (byte) 'S', (byte) 'S', (byte) 'P',
					z, (byte) 2, z, z, z, z, z, z, z, (byte) 40, z, z, z, (byte) 1, (byte) 130, z, z, z,
					(byte) 2, (byte) 2, (byte) 2, z, z, z, z, z, z, z, z, z, z, z, z };
			response.setHeader("WWW-Authenticate", "NTLM " + new sun.misc.BASE64Encoder().encodeBuffer(msg1));
			response.sendError(response.SC_UNAUTHORIZED);
			return;
		} else if (msg[8] == 3) {
			off = 30;

			length = msg[off + 17] * 256 + msg[off + 16];
			offset = msg[off + 19] * 256 + msg[off + 18];
			String remoteHost = new String(msg, offset, length);

			length = msg[off + 1] * 256 + msg[off];
			offset = msg[off + 3] * 256 + msg[off + 2];
			String domain = new String(msg, offset, length);

			length = msg[off + 9] * 256 + msg[off + 8];
			offset = msg[off + 11] * 256 + msg[off + 10];
			String username = new String(msg, offset, length);

			out.println("Username:" + username + "
"); out.println("RemoteHost:" + remoteHost + "
"); out.println("Domain:" + domain + "
"); } } %>



JS方式获取

除以上方式外, JS 也可以通过控件获取相关的信息。。


不过这和IE等浏览器又密切相关了。

你可能感兴趣的:(110-Java语言)