sso与spring security

1.spring securiy多认证
  1. 定制化认证过滤器

    继承AbstractProcessingFilter

    @Override
    	public Authentication attemptAuthentication(HttpServletRequest httpServletRequest) throws AuthenticationException {
    	//认证逻辑,可以参考org.springframework.security.ui.webapp.AuthenticationProcessingFilter实现
    	return getAuthenticationManager().authenticate(new OpenIdAuthenticationToken(token, sign, requestId));
    	//这里OpenIdAuthenticationToken类很重要,不同的认证方式构造不同的认证实体类
    	}
    	
    	protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
    	//认证前置处理
        }
    	
    	@Override
    	protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException {
    	//认证成功后处理逻辑
    	}
    	
    	protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
    	//认证失败后处理逻辑
        }
    	
    	@Override
    	//声明拦截路径,即认证url,通过配置不同的登陆url来支持多种认证
    	//针对特定url进行拦截认证处理
    	public String getDefaultFilterProcessesUrl() {
    		return "/singlePointLand/validate.do";
    	}
    
    	@Override
    	//过滤链中位置
    	public int getOrder() {
    		return FilterChainOrder.AUTHENTICATION_PROCESSING_FILTER;
    	}
    

    前后端分离处理

    重写
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
    	//写入认证返回结果
    	request.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("login success")
    }
    
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
    	//写入认证返回结果
    	request.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("login fail")
    }
    
  2. 将过滤器注入到security过滤链

    <bean id="capaaAuthenticationProcessingFilter" class="com.hzmc.capaa.web.security.CapaaSeparationAuthenticationProcessingFilter" autowire="byType">
    		<security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
    		<!-- defaultTargetUrl默认跳转url,这个必填 -->
    		<property name="defaultTargetUrl" value="/auditsession/mainpage.do" />
    </bean>
    
  3. 定制化令牌domain类

    不同的认证方式需要定制化不同的认证实体类,org.springframework.security.providers.Authentication的实现或者继承org.springframework.security.providers.AbstractAuthenticationToken

  4. 定制化认证provider

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    	//TODO 认证逻辑
    }
    
    @Override
    public boolean supports(Class aClass) {
    	//TODO 当前provider认证启用的条件
    }
    

    至于原理是由于getAuthenticationManager()的实现org.springframework.security.providers.ProviderManager的doAuthentication认证方法

    public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
            Iterator iter = this.providers.iterator();
            Class toTest = authentication.getClass();
            Object lastException = null;
    
            while(iter.hasNext()) {
                AuthenticationProvider provider = (AuthenticationProvider)iter.next();
                //这里会调用Provider的supports用于判断是否调用改Provider的认证方法
                if (provider.supports(toTest)) {
                }
            }
    }
    
2.SSO
简介

​ 在日常生活中,很多人由于忘记某些网站的登录密码而烦恼,因为大多数用户都要记忆不少于10个用户名和相应密码。为了便于记忆,很多人都在不同的站点使用相同的用户名和密码,虽然这样可以减少负担,但是同时也降低了安全性,而且使用不同的站点同样要进行多次登录。同时,随着信息化飞速发展,大型企业和政府部门等都开始使用电子系统进行办公,而且整个办公系统由多个不同的子系统构成,如办公自动化(OA)系统,财务管理系统,档案管理系统,信息查询系统等。如果每个系统都使用独立的登录和验证机制,那么每天工作人员都要登录不同的系统进行办公。用户登录的频繁操作,降低了员工的工作效率,造成工作成本的浪费。而大量的密码和用户名的记忆时间长了也会出现问题,忘记密码或者混淆密码都会造成很大的麻烦。基于以上原因,为用户提供一个畅通的登录通道变得十分重要。

​ 单点登录(SingleSign-On,SSO)是一种帮助用户快捷访问网络中多个站点的安全通信技术。单点登录系统基于一种安全的通信协议,该协议通过多个系统之间的用户身份信息的交换来实现单点登录。使用单点登录系统时,用户只需要登录一次,就可以访问多个系统,不需要记忆多个口令密码。单点登录使用户可以快速访问网络,从而提高工作效率,同时也能帮助提高系统的安全性。

实现机制

​ 当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录;根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返回给用户一个认证的凭据--ticket;用户再访问别的应用的时候就会将这个ticket带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行校验,检查ticket的合法性。如果通过校验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。

要实现SSO,需要以下主要的功能:

系统共享

​ 统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行校验,判断其有效性。

信息识别

​ 要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。

另外:

​ 1、单一的用户信息数据库并不是必须的,有许多系统不能将所有的用户信息都集中存储,应该允许用户信息放置在不同的存储中,事实上,只要统一认证系统,统一ticket的产生和校验,无论用户信息存储在什么地方,都能实现单点登录。

​ 2、统一的认证系统并不是说只有单个的认证服务器

当用户在访问应用系统1时,由第一个认证服务器进行认证后,得到由此服务器产生的ticket。当他访问应用系统2的时候,认证服务器2能够识别此ticket是由第一个服务器产生的,通过认证服务器之间标准的通讯协议(例如SAML)来交换认证信息,仍然能够完成SSO的功能。

常见问题
  1. session不共享问题

    解决系统之间Session不共享问题有一下几种方案:

    • Tomcat集群Session全局复制(集群内每个tomcat的session完全同步,会影响集群的性能)
    • 根据请求的IP进行Hash映射到对应的机器上(请求的IP一直会访问同一个服务器,宕机后丢失很多session)
    • 把session数据放在缓存中,如:redis
  2. Cookie跨域问题

    cookie局限

    • 浏览器最多保存300个cookie
    • 为单个web服务器的最多只能保存20个cookie
    • 每个cookie不能超过4000个字节
    • 不能跨域访问

    针对Cookie存在跨域问题,有几种解决方案:

    • url重写,将登录成功后返回的token拼接到url中
    • Cookie的域设置为顶域,这样子域都能访问到(需要保持顶域一样,如:a.mchz.com与b.mchz.com,设置cookie的domain为.mchz.com)
    • nginx反向代理
3.LDAP与AD
LDAP概念原理
  1. 什么是目录
  • 目录是一类为了浏览和搜索数据的特殊的数据库,例如:最知名的的微软公司的活动目录(active directory)就是目录数据库的一种。目录服务是按照树状形式存储信息的,目录包含基于属性的描述性信息,并且支持高级的过滤功能。
  • 一般来说,目录不支持大多数事务性数据库所支持的高吞吐量和复杂的更新操作。目录进行更新操作,可以说是要么全部,要么都不的原子操作,目录服务适合的业务应用在于提供大量的查询和搜索操作
  • 为了保证目录数据的可用性和可靠性,在确保提供快速的查询和搜索操作的同时,还提供了主从服务器同步目录数据信息的能力,这相当于传统的Mysql数据库的主从同步一样,可以最大限度的确保基于目录业务的持续可用性
  • 广义的目录服务概念,可以有多重不同的方式来提供目录服务,不同的目录所允许存储的信息是不同的,在信息如何被引用,查询,更新以及防止未经授权的访问等问题上,不同的目录的处理方式也有诸多的不同。一些目录服务是本地的,只提供受限的服务,(比如,单机上的finger服务)。另一些服务是大范围的(global),提供广阔得多的服务(比如面向整个因特网)。大范围的服务通常是分布式的,这也就意味着数据是分布在多台机器上的,这些机器一起来提供目录服务。典型的大范围服务定义一个统一的命名空间(namespace)来给出一个相同的数据视图(data view),而不管你相对于数据所在的位置。DNS是一个典型的大范围分布式目录服务的例子
  1. 什么是LDAP

    • LDAP是Lightweight Directory Access Protocol (轻量级目录访问协议)的缩写。正如它的名字所表明的那样,它是一个轻量级的目录访问协议,特质基于X.500的目录访问协议的简化版本。LADP运行在TCP/IP或者其他的面向连接的传输服务至上。LADP完整的技术规范由RFC2251 “The Lightweight Directory Access Protocol(V3)”和其他几个在RFC3377中定义的文档组成。
    • 目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。
    • 目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。
  2. LDAP目录服务特点

    • LDAP是一个跨平台的,标准的协议,近几年来得到了业界广泛的认可
    • LADP的结构用树形结构来表示,而不是用表格。因此不用SQL语句维护了
    • LADP提供了静态数据的快速查询方式,但在写数据方面并不擅长
    • LADP服务可以使用基于“推或"拉"的复制信息技术,用简单的基于安全证书的安全认证,复制部分或全部数据,既保证了数据的安全性,又提高了数据的访问效率
    • LDAP是一个安全的协议,LDAP v3支持SASL(Simple Authentication and Securityh Layer),SSL(Secure Socket Layer)和TLS(Transport Layer Security),使用认证来确保事务的安全,另外,LDAP提供了不同层次的访问控制,以限制不同用户的访问权限
    • LADP支持数据存储,LADP存储的数据可由是文本资料,二进制图片等
    • Client/Server模型:Server用于存储树,Client提供操作目录信息树的工具,这些工具可以将数据库的内容以文本格式(LDAP数据交换格式,LDIF)呈现在我们的面前
    • LDAP是一种开放Internet标准,LADP协议是跨平台的Internt协议,它是基于X.500标准的,与X.500不同,LADY支持TCP/IP(即可以分布式部署)
  3. LDAP的基本模型

    每一个系统、协议都会有属于自己的模型,LDAP也不例外,在了解LDAP的基本模型之前我们需要先了解几个LDAP的目录树概念:

    (一)目录树概念

    1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。

    2. 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。

    3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。

    4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。

    (二)基本模型
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

AD概念原理

1. AD 域服务

什么是目录(directory)呢?

日常生活中使用的电话薄内记录着亲朋好友的姓名、电话与地址等数据,它就是 telephone directory(电话目录);计算机中的文件系统(file system)内记录着文件的文件名、大小与日期等数据,它就是 file directory(文件目录)。

如果这些目录内的数据能够由系统加以整理,用户就能够容易且迅速地查找到所需的数据,而 directory service(目录服务)提供的服务,就是要达到此目的。在现实生活中,查号台也是一种目录;在 Internet 上,百度和谷歌提供的搜索功能也是一种目录服务。

Active Directory 域内的 directory database(目录数据库)被用来存储用户账户、计算机账户、打印机和共享文件夹等对象,而提供目录服务的组件就是 Active Directory (活动目录)域服务(Active Directory Domain Service,AD DS),它负责目录数据库的存储、添加、删除、修改与查询等操作。一般适用于一个局域网内。

在 AD 域服务(AD DS)内,AD 就是一个命名空间(Namespace)。利用 AD,我们可以通过对象名称来找到与这个对象有关的所有信息。

在 TCP/IP 网络环境内利用 Domain Name System(DNS)来解析主机名与 IP 地址的对应关系,也就是利用 DNS 来解析来得到主机的 IP 地址。除此之外,AD 域服务也与 DNS 紧密结合在一起,它的域命名空间也是采用 DNS 架构,因此域名采用 DNS 格式来命名,例如可以将 AD 域的域名命名为 moonxy.com。

2. AD域对象与属性

AD 域内的资源以对象(Object)的形式存在,例如用户、计算机与打印机等都是对象,而对象则通过属性(Attriburte)来描述其特征,也就是说对象本身是一些属性的集合。例如,创建一个账户张三,则必须添加一个对象类型(object class)为用户的对象(也就是用户账户),然后在这个用户账户内输入张三的姓名、登录账户、电话号码和电子邮件等信息,这其中的用户账户就是对象,而姓名、登录账户等数据就是该对象的属性,张三就是对象类型为用户(user)的对象。

3. AD 域控制器 DC

AD 域服务(AD DS)的目录数据存储在**域控制器(Domain Controller,DC)**内。一个域内可以有多台域控制器,每台域控制器的地位几乎是平等的,它们各自存储着一份几乎完全相同的 Active Directory。当在任何一台域控制器内添加了一个用户账户后,此账户默认被创建在此域控制器的 Active Directory,之后会自动被复制(replicate)到其他域控制器的 Active Directory,以便让所有域控制器内的 Active Directory 数据都能够同步(synchronize)。

当用户在域内某台计算机登录时,会由其中一台域控制器根据其 Active Directory 内的账户数据,来审核用户输入的账户与密码是否正确。如果是正确的,用户就可以登录成功;反之,会被拒绝登录。域控制器是由服务器级别的额计算机来扮演的,例如 Windows Server 2012 和 Windows Server 2008 R2 等。

通常,域控制器的 Active Directory 数据库是可以被读写的,除此之外,还有 Active Directory 数据库是只可以读取、不可以被修改的只读域控制器(Read-Only Domain Controller,RODC)。例如,某子公司位于远程网络,如果安全措施并不像总公司一样完备,则可以使用 RODC。

4. 标识名称

​ 它是对象在 Active Directory 内的完整路径,DN 有三个属性,分别是 CN,OU,DC

DC (Domain Component):域名组件;

CN (Common Name):通用名称,一般为用户名或计算机名;

OU (Organizational Unit):组织单位;

​ 例如,如上用户账户,其 DN 为:

​ CN=张三,OU=Web前端组,OU=软件开发部,DC=moonxy,DC=com

​ 其中 **DC(Domain Component)**表示 DNS 域名中的组件,例如 moonxy.com 中的 moonxy 与 com;OU为组织单位(Organization Unit)CN为通用名称(Common Name)*,一般为用户名或服务器名*。除了DC与OU之外,其他都利用CN来表示,例如用户与计算机对象都属于CN。上述DN表示法中的 moonxy.com 为域名,软件研发部、Web前端组都是组织单位。此 DN 表示账户张三存储在 moonxy.com\软件研发部\Web前端组路径中。

相对标识名称(Relative Distinguished Name,RDN):RDN用来代表DN完整路径中的部分路径,例如上面路径中的 CN=张三与 OU=Web前端组等都是 RDN。

Base DN:LDAP 目录树的最顶部就是根,也就是所谓的 “Base DN”,如 “DC=moonxy,DC=com”。

除了 DN 与 RDN 这两个对象名称外,另外还有如下两个名称:

全局唯一标识符(Global Unique Identifier,GUID):GUID 是一个128位的数值,系统会自动为每个对象指定一个唯一的GUID。虽然可以改变对象的名称,但是其GUID永远不会改变。

用户主体名称(User Principal Name,UPN):每个用户还可以有一个比DN更短、更容易记忆的 UPN,例如上面的张三隶属于 moonxy.com,则其 UPN 可以为 zhangsan@moonxy.com。用户登录时所输入的账户名最好是 UPN,因为无论此用户的账户被移动到哪一个域,其 UPN 都不会改变,因此用户可以一直使用同一个名称来登录。

AD 与 LDAP 的关系:LDAP 是一种用来访问 AD 数据库的目录服务协议,AD DS 会通过 LDAP 名称路径来表示对象在 AD 数据库中的位置,以便用它来访问 AD 数据库内的对象。LDAP 的名称路径包括有 DN、RDN。

​ **openLDAP(Linux),Active Directory(Microsoft)**等是对 LDAP 目录访问协议的具体实现,除了实现协议的功能,还对它进行了扩展。

Java代码示例

1.测试连接(AD与LDAP均适用)

@Test
public void validateLdapConnect(){
    	try{
            Hashtable<String, String> HashEnv = new Hashtable<String, String>();
			HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); // 访问安全级别(none,simple,strong);
			HashEnv.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=mchz,dc=com"); // 用户名
			HashEnv.put(Context.SECURITY_CREDENTIALS, "hzmcdba"); // 密码
			HashEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); // 工厂类
			HashEnv.put("com.sun.jndi.ldap.connect.timeout", "3000");// 连接超时设置为3秒
			HashEnv.put(Context.PROVIDER_URL, "ldap://" + "192.168.230.81" + ":" + "389");// 默认端口389
			DirContext ctx = new InitialDirContext(HashEnv);// 初始化上下文
            LdapUtils.closeContext(ctx);//关闭连接
			log.info("测试连接成功!");
        }catch(Exception e){
            log.error("验证服务器连接失败,错误:" + ExceptionUtils.getFullStackTrace(e));
        }
	}

2.认证

@Test
	public void validateLdapForJavaTest() throws NamingException {
		DirContext ctx = null;
		Hashtable<String, String> HashEnv = new Hashtable<String, String>();
		HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); // 访问安全级别(none,simple,strong);
		HashEnv.put(Context.SECURITY_PRINCIPAL, "uid=lhf3,ou=dev,ou=users,dc=mchz,dc=com"); // 用户名,ldap时用的是dn,ad时用的是name
		HashEnv.put(Context.SECURITY_CREDENTIALS, "test"); // 密码
		HashEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); // LDAP工厂类
		HashEnv.put("com.sun.jndi.ldap.connect.timeout", "3000");// 连接超时设置为3秒
		HashEnv.put(Context.PROVIDER_URL, "ldap://" + "192.168.230.81" + ":" + "389");// 默认端口389
		ctx = new InitialDirContext(HashEnv);// 初始化上下文
	}

3.查询

@Test
public void pageLdapTemplateSearch() throws IOException {
		ldapTemplate = new LdapTemplate(ldapContextSource());
		int pageSize = 2;
		String userPricipalName = "sAMAccountName";
		PagedResultsCookie cookie = null;
		AndFilter filter = new AndFilter();
		filter.and(new LikeFilter(userPricipalName, "*"));
		filter.and(new EqualsFilter("objectCategory", "person"));
		filter.and(new EqualsFilter("objectClass", "user"));
		PagedResultsDirContextProcessor control = new PagedResultsDirContextProcessor (pageSize, cookie);
		SearchControls searchControls = new SearchControls();
		searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
		searchControls.setReturningAttributes(new String[]{userPricipalName});

		ContextMapper<String> contextMapper = new ContextMapper<String>() {
			@Override
			public String mapFromContext(Object ctx) throws NamingException {
				DirContextOperations adapter = (DirContextOperations)ctx;
				return (String) adapter.getObjectAttribute(userPricipalName);
			}
		};
		do {
			List<String> mList = ldapTemplate.search("CN=Users",  filter.encode(),searchControls, contextMapper, control);
			cookie = control.getCookie();
			System.out.println(mList);
		} while (cookie.getCookie() != null);
}

public LdapContextSource ldapContextSource(){
        LdapContextSource contextSource = new LdapContextSource();
        Map<String, Object> config = new HashMap<>();

        contextSource.setUrl(ldapPropertyConfig.getLdapUrl());
        contextSource.setBase(ldapPropertyConfig.getBaseDc());
        contextSource.setUserDn(ldapPropertyConfig.getLdapUserName());
        contextSource.setPassword(ldapPropertyConfig.getLdapPassword());

        config.put("java.naming.ldap.attributes.binary", "objectGUID");
        config.put(Context.SECURITY_AUTHENTICATION, "simple");

        contextSource.setPooled(true);
        contextSource.setBaseEnvironmentProperties(config);
        contextSource.afterPropertiesSet();

        return contextSource;
    }
    

参考:https://www.jianshu.com/p/27ab7a5d56a4
https://www.cnblogs.com/wilburxu/p/9174353.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值