这篇博客文章主要是关于Spring Security配置的。
更具体地说,它打算显示如何在一个Web应用程序中配置两个不同的安全领域。

第一安全领域是针对浏览器客户端的。 它使我们能够在登录页面中登录并访问受保护的资源。

第二安全领域旨在处理来自android应用程序的REST Web服务请求。 在每个请求上,REST客户端应将所需的信息发送到服务器,并且此信息将用于确定是否应允许RESTfull请求通过。
这两个安全领域(配置)的区别在于Web应用程序中资源的URL模式不同。 在这两种配置中,我们都可以重用相同的身份验证逻辑。

第一安全领域

我们有一个经典的Web应用程序,其中包含一些受保护的资源(页面)。 为了访问这些资源,用户应在登录页面上登录到应用程序。 如果登录成功,则将用户转发到所请求的资源。 如果用户的登录过程由于某种原因(例如,错误的用户名或密码)而失败,则该用户将无法获得受保护的资源,并且将其重定向到登录页面,并显示相应的消息。
我在上一节中刚刚描述的情况可能被认为是“经典的Web应用程序行为”。 普通的互联网用户至少会遇到数百个这样的在线应用程序。 这种行为旨在与客户(浏览器)一起使用。 由于这种行为在当今非常普遍,因此Spring安全性使得实现它非常容易。 显然,基于表单的身份验证机制最适合我们。 在Spring Security中,当您希望定义与客户端身份验证状态相关的操作时,可以定义入口点。 这是我们标准浏览器-客户端入口点的预览:

<http  entry-point-ref="loginUrlAuthenticationEntryPoint" use-expressions="true"><intercept-url pattern="/includes/content/administration.jsp" access="hasAnyRole('ROLE_100','ROLE_1000')" /><intercept-url pattern="/includes/content/userAdministration.jsp" access="hasAnyRole('ROLE_100','ROLE_1000')" /><intercept-url pattern="/includes/content/groupAdministration.jsp" access="hasAnyRole('ROLE_100','ROLE_1000')" /><intercept-url pattern="/includes/content/departmentAdministration.jsp" access="hasAnyRole('ROLE_100','ROLE_1000')" /><intercept-url pattern="/includes/content/shiftAdministration.jsp" access="hasAnyRole('ROLE_100','ROLE_101','ROLE_1000') /><custom-filter position="FORM_LOGIN_FILTER" ref="userAuthenticationProcessingFilter" /><logout logout-url='/logout' />
</http><beans:bean id="loginUrlAuthenticationEntryPoint"class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"><beans:property name="loginFormUrl" value="/login.jsp" />
</beans:bean>

希望这是不言而喻的。 loginUrlAuthenticationEntryPoint是一个入口点,您可以在其中配置实现了登录功能的登录页面。 然后,在http元素中,我们将该入口点的行为配置为更多详细信息。 首先,我们定义了拦截URL元素列表。 仅当请求了这些资源之一时,才会激活此入口点。 我们还用我们自己的自定义版本替换了默认的FORM_LOGIN_FILTER 。 Spring安全性通过应用您在入口点中定义的过滤器链来发挥作用。 这些基本上是标准的servlet过滤器。 您可以使用Spring预定义过滤器,也可以扩展它们并插入自定义过滤器。 在这里,我们使用了Spring的安全过滤器之一。

这是一个UsernamePasswordAuthenticationFilter 。 它用于具有用户名和密码字段的登录页面的情况。 该过滤器使我们能够结合将用于身份验证的自定义机制。 它还允许我们定义在成功和不成功的身份验证的情况下将要采取的措施。 让我们看看这种配置如何:

<beans:bean id="loginSuccessHandler"class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"><beans:property name="defaultTargetUrl" value="/main.jsp" />
</beans:bean><beans:bean id="userAuthenticationProcessingFilter"class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"><beans:property name="authenticationManager" ref="authenticationManager" /><beans:property name="authenticationFailureHandler"ref="loginMappingFailureHandler" /><beans:property name="authenticationSuccessHandler"ref="loginSuccessHandler" />
</beans:bean><beans:bean id="loginMappingFailureHandler"class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler"><beans:property name="exceptionMappings" ref="failureUrlMap" />
</beans:bean><util:map id="failureUrlMap" map-class="java.util.HashMap"><beans:entrykey="org.springframework.security.authentication.BadCredentialsException"value="/login.jsp?errorMessage=bad.credentials" /><beans:entrykey="org.springframework.security.authentication.DisabledException"value="/login.jsp?errorMessage=disabled.user" />
</util:map>

我们再来看一下此配置。 我将解释我们在这里所做的事情。
首先,我们定义了表单登录过滤器。 实际上,我们为此定义了三件事。 我们为它提供了自定义身份验证机制,该机制将在整个应用程序中使用。 该机制通过authenticationManager插入到过滤器中。 我将很快谈论认证管理器。

第二件事,我们定义了一个登录失败处理程序。 基本上,这是Spring异常以及对这些异常采取的措施的映射。 异常由AuthenticationProvider引发,如下所述。 例如,当用户输入错误的用户名或密码时,将引发BadCredentialsException 。 发生这种情况时,用户将再次重定向到登录页面。 还将某些参数附加到登录页面的URL,以使我们能够显示正确的错误消息。

第三,也是最后一件事,我们定义了一个成功的身份验证处理程序。 这确实很明显。 我们正在定义如果登录成功该怎么办。 用户被发送到主页。

现在,让我们来谈谈认证管理器。 这只是Spring使用的接口。 可以是任何东西。 它可以是用户数据库,LDAP服务器或其他数据库。 身份验证管理器不能自行完成工作。 它仅使用身份验证提供程序为其执行实际的身份验证工作。 身份验证提供程序在被调用时可以做两件事:

  1. 可以返回成功填充的对象(这是Spring的Authentication接口的实例)
  2. 可以抛出适当的Spring安全异常之一

身份验证管理器配置如下所示:

<authentication-manager alias="authenticationManager"><authentication-provider ref="authenticationProvider" />
</authentication-manager><beans:bean id="authenticationProvider" class="ba.codecentric.medica.security.UserAuthenticationProvider"><beans:property name="userService" ref="userService"/><beans:property name="licenseInformationWrapper" ref="licenseInformationWrapper"/>
</beans:bean>

这是我的定制身份验证提供程序的源代码:

package ba.codecentric.medica.security;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;import org.apache.commons.collections.CollectionUtils;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;import ba.codecentric.medica.administration.service.UserService;
import ba.codecentric.medica.model.Group;
import ba.codecentric.medica.model.LicenseInformationWrapper;
import ba.codecentric.medica.model.Role;
import ba.codecentric.medica.model.User;public class UserAuthenticationProvider implements AuthenticationProvider {private static final String ROLE_PREFIX = "ROLE_";private UserService userService;private LicenseInformationWrapper licenseInformationWrapper;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {User user = userService.getUserByUsernameAndPassword(authentication.getName(), authentication.getCredentials().toString(), true);if (user != null) {Collection authorities = new ArrayList(buildRolesFromUser(user));authorities.addAll(getActivatedModulesAsRoles());return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(),authorities);} else {throw new BadCredentialsException("Try again");}}private Collection getActivatedModulesAsRoles() {List activatedModules = new ArrayList();if(CollectionUtils.isNotEmpty(licenseInformationWrapper.getActivatedModules())) {for(String activatedModuleName: licenseInformationWrapper.getActivatedModules()) {activatedModules.add(new SimpleGrantedAuthority(ROLE_PREFIX + activatedModuleName));}}return activatedModules;}private Collection buildRolesFromUser(User user) {Collection authorities = new HashSet();for (Group group : user.getGroups()) {for (Role role : group.getRoles()) {authorities.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.getName()));}}return authorities;}@Overridepublic boolean supports(Class authentication) {return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));}public UserService getUserService() {return userService;}public void setUserService(UserService userService) {this.userService = userService;}public LicenseInformationWrapper getLicenseInformationWrapper() {return licenseInformationWrapper;}public void setLicenseInformationWrapper(LicenseInformationWrapper licenseInformationWrapper) {this.licenseInformationWrapper = licenseInformationWrapper;}}

如您所见,身份验证过程非常简单。 我的自定义身份验证提供程序实现了Spring AuthenticationProvider接口。

就像我们之前讨论的那样,它可以完成工作。 它在数据库的用户表中查找用户名和密码。 如果找到这样的用户,则创建并返回认证对象。 否则,如果未找到此类用户,则authenticate方法将引发适当的异常。 还有一件事情。 Spring使用GrantedAuthority对象的集合来表示分配给用户的角色。 这就是为什么我们构建这样的集合并将其附加到身份验证对象的原因。 必须将数据库中与用户连接的每个角色都添加到已授予权限的集合中,以使Spring将此角色视为角色。 并且每个角色必须具有ROLE_前缀。 我们还有另一件事要展示。 如何从登录网页调用此过滤器? 这是login.jsp的一部分:

<form id="loginForm" method="POST" action="j_spring_security_check"><table>
<tr><td><b><fmt:message key="login.username.label" />:</b></td><c:choose><c:when test="${not empty param.j_username}" ><td><input type="text" name="j_username" id="username" value="${param.j_username }" class="loginInput" /></td>    </c:when><c:otherwise><td><input type="text" name="j_username" id="username" class="loginInput"/></td></c:otherwise></c:choose> </tr>
<tr><td><b><fmt:message key="login.password.label" />:</b></td><c:choose><c:when test="${not empty param.j_password}" ><td><input type="password" name="j_password" id="password" value="${param.j_password }" class="loginInput" /></td>    </c:when><c:otherwise><td><input type="password" name="j_password" id="password" class="loginInput" /></td></c:otherwise></c:choose> </tr>
<tr><td><b><fmt:message key="login.alphabet.label" /></b>:</td><td><select id="alphabet" class="fullWidth" onchange="languageSelect();"><option value="lat"><fmt:message key="login.alphabet.lat" /></option><option value="cir"><fmt:message key="login.alphabet.cyr" /></option></select></td></tr>
<tr><td></td><td><input type="submit" value="<fmt:message key="login.button.label" />" class="right"></td></tr>
</table></form>

默认情况下,标准Spring安全性设置要求您通过调用j_spring_security_check从登录表单中调用安全链。 用户名和密码过滤器将拦截此URL(默认设置),但您可以将其配置为拦截任何其他URL。 好吧,这一切都与“基于浏览器的客户端”安全领域有关。 如果用户未登录并尝试访问受该领域(入口点)保护的资源,则该领域将重定向用户到登录页面并要求他登录。只有当用户登录时,才受保护资源将可用。

第二安全领域

现在最后,让我们讨论应用程序中的第二个安全领域。 我们仅在博客的简介部分提到了它。 该应用程序支持REST服务调用。 我们必须实现将应用程序的某些部分与运行在移动设备上的简单android应用程序同步的要求。 我们认为最简单的方法是从手机到Web应用程序进行RESTfull调用。 当然,我们在这里也需要安全。 我们不想让用户始终能够连接到应用程序。 用户列表及其角色在数据库中维护。 例如,某个用户今天可以处于活动状态,但是明天管理员可以决定该用户不再处于活动状态,并且不应该能够连接到应用程序(也应该不能登录)。 因此,必须在REST服务领域中实现安全性。

让我们考虑一下这个领域。 这些REST调用应该如何工作。 Android应用程序将POST请求(RESTfull请求)发送到Web应用程序以获取某些数据(医生的约会等)。 应用程序查找并返回请求的数据。 然后,Android应用程序处理获取的数据并将其显示给用户。

现在,我们将安全性添加到此RESTfull概念中,并尝试用安全性描述概念。 Android应用程序发送POST请求。 Android应用程序发送一个包含哈希用户名和密码的标头,作为每个请求的一部分(请参阅-> 基本身份验证 )。

Web应用程序的安全领域(入口点)应该接收到此请求,并且如果用户名和密码确实是活动用户,则允许此请求到达Web应用程序中的REST服务,并将对其进行处理。 如果用户名和密码无效(或用户处于非活动状态),则请求应在安全入口点失败,这意味着我们应立即返回格式正确的HTTP响应,该响应将通知客户端应用程序该用户与该用户名称和密码不允许访问Web应用程序中的REST服务。

正如我们在这种情况下所看到的,先前定义的入口点的行为与REST服务不对应。 上一个入口点,如果用户未通过身份验证,则将其重定向到登录页面。 这意味着,如果用户未通过身份验证,则服务器实际上会返回包含登录页面HTML代码的HTTP响应。 我们无法在android应用程序中处理这种行为,因为它不会显示任何HTML网页。 那么,当它收到登录页面HTML时会怎么做?
这就是为什么我们实际上需要Web应用程序中的第二个安全域(安全性入口点)的主要原因,该安全域的工作方式不同于处理浏览器客户端的机制。 如果用户无法通过身份验证,此新的安全领域将仅将格式正确的HTTP响应返回给客户端应用程序(它将在响应上设置特定的HTTP状态和HTTP消息)。 我们知道,在Java Server环境中,我们有一种称为基本身份验证的安全性。 它基于发送散列的用户名和密码作为请求标头的一部分(标头必须以特定方式进行格式化)。 然后,如果可以在用户数据池中找到用户名和密码,则允许通过请求。 否则,将返回HTTP响应以及相应的状态和消息,以告知客户端不允许其访问某些资源。 对我们来说幸运的是,Spring支持这种身份验证机制。 我们将添加另一个入口点和一个过滤器。 它将是这样的:

<http entry-point-ref="basicAuthEntryPoint" pattern="/ws/**" use-expressions="true"><intercept-url pattern="/ws/schedule/patients" access="hasAnyRole('ROLE_1','ROLE_100','ROLE_300','ROLE_1000')" /><custom-filter ref="basicAuthenticationFilter" after="BASIC_AUTH_FILTER" />
</http><beans:bean id="basicAuthEntryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint"><beans:property name="realmName" value="REST Realm" />
</beans:bean><beans:bean id="basicAuthenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"><beans:property name="authenticationManager" ref="authenticationManager"/><beans:property name="authenticationEntryPoint" ref="basicAuthEntryPoint" />
</beans:bean>

基本上,我们添加了一个新的入口点(安全领域),该入口点拦截URL路径/ ws / **上的所有请求。 这是我们所有REST服务调用通过的路径。 我们使用了Springs BasicAuthenticationFilter ,它提供了读取请求标头和调用身份验证管理器的功能。 如果在数据库中找到了用户名和密码(由身份验证管理器处理),将允许请求进一步传递。 如果在数据库中找不到用户名和密码,入口点将在HTTP响应上设置状态401,并立即将此响应返回给客户端。 这只是我们Android应用程序所需的行为。

这就是我们的应用程序需要的所有安全配置。 现在剩下要做的就是在web.xml文件中启用Spring安全过滤器。 我已经提到过,Spring安全性可以通过在请求上调用过滤器链来实现。 这意味着存在某种“主”过滤器,它是所有其他后续过滤器和服务的基础。 在web.xml中启用并配置了该“主”过滤器。 这是我的配置:

<filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern><dispatcher>ERROR</dispatcher><dispatcher>REQUEST</dispatcher><dispatcher>INCLUDE</dispatcher><dispatcher>FORWARD</dispatcher>
</filter-mapping>

如您所见,主要的Spring安全过滤器已配置为拦截对应用程序中所有资源的所有请求。 但是,哪些资源真正受到保护,哪些资源是公共资源,则由入口点控制(通过http元素中的URL模式)。 例如,位于/ css文件夹中的所有资源都被视为公用资源,不需要用户进行身份验证就可以访问它们:

<http pattern="/css/**" security="none" />

另一方面,像管理页面这样的资源受到保护,并且如果用户希望访问该页面,则不仅要求用户进行身份验证,还需要分配某些角色。 这是此xml代码段中的示例:

<!-- more xml -->
<intercept-url pattern="/includes/content/administration.jsp" access="hasAnyRole('ROLE_100','ROLE_1000')" />
<!-- more xml -->

还有两件事要说!

当您的安全配置中有多个http元素时,请确保具有最特定模式属性的元素位于不那么特定甚至可能没有模式属性的元素之前。 否则,当Spring开始抱怨应用程序中的过滤器排序没有意义时,您将在日志文件中看到很长的堆栈跟踪。

阅读此博客后,您可能会开始认为添加基于表单的身份验证或基本身份验证就足够了,您的应用程序将是安全的。 但是,那不是完全正确的。 任何对HTTP协议和网络具有“技术”知识的人都可以想到如何在网络内部拦截HTTP数据流的方式。 对于基本身份验证和基于表单的身份验证,诸如用户名和密码之类的信息直接通过HTTP协议发送。 对于基本身份验证,它们将作为HTTP请求标头发送。 对于基于表单的身份验证,它们将作为请求参数发送。 因此,可以拦截和读取这些HTTP流的人员可以轻松读取您的标头并请求参数。 稍后,同一个人可以手动创建请求,并将那些标头或参数附加到请求。 当然,此新请求现在将由容器授权,因为它包含正确的身份验证详细信息。

那么,我们该怎么做才能避免对应用程序的这些安全攻击?
真正的答案是:我们应该在保护应用程序资源的地方使用HTTPS协议 。 仅通过使用HTTPS协议和Java服务器的身份验证机制,我们就可以肯定地说我们的应用程序确实是安全的。

参考: Spring Security –我们的JCG合作伙伴 Branislav Vidovi提供了一个应用程序中的两个安全领域 ? 在极客的东西:-)博客上。

翻译自: https://www.javacodegeeks.com/2012/08/spring-security-two-security-realms-in.html

Spring Security –在一个应用程序中有两个安全领域相关推荐

  1. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

  2. Spring Security 参考手册(一)

    Spring Security 参考手册 Ben AlexLuke TaylorRob WinchGunnar Hillert Spring security 是一个强大的和高度可定制的身份验证和访问 ...

  3. Spring Security 详解与实操第一节 认证体系与密码安全

    开篇词 Spring Security,为你的应用安全与职业之路保驾护航 你好,我是鉴湘,拉勾教育专栏<Spring Cloud 原理与实战><Spring Boot 实战开发> ...

  4. Spring Security中文文档

    Spring Security中文文档 来源:https://www.springcloud.cc/spring-security.html#overall-architecture 作者 Ben A ...

  5. Spring Security并没有那么难嗷 简单理解OAuth2.0

    文章目录 1. 基本概念 1.1 什么是认证 1.2 什么是会话 1.3 什么是授权 1.4 授权的数据模型 1.5 RBAC 1.5.1 基于角色的访问控制 1.5.2 基于资源的访问控制 2. 基 ...

  6. Spring Security OAuth 2开发者指南译

    Spring Security OAuth 2开发者指南译 介绍 这是用户指南的支持OAuth 2.0.对于OAuth 1.0,一切都是不同的,所以看到它的用户指南. 本用户指南分为两部分,第一部分为 ...

  7. 使用Spring Security进行简单身份验证

    朋友不允许朋友写用户身份验证. 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK. 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护. 身份验证对于除了最基本的Web应 ...

  8. Spring Security学习总结

    2019独角兽企业重金招聘Python工程师标准>>> 1.Spring Security介绍  一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权 ...

  9. spring security xml配置官方详解

    6. Security Namespace Configuration 6.1 Introduction 自2.0版本的spring框架以来,命名空间配置已可用. 它允许您使用来自附加XML模式的元素 ...

最新文章

  1. linux下安装hadoop
  2. 准官宣?余承东证实华为已有自研操作系统
  3. 南京铁道学院计算机应用,南京铁道职业技术学院交通运营管理专业怎么样
  4. Leetcode--141. 环形链表
  5. 华为机试HJ91:走方格的方案数
  6. c语言运算优先级与结合方向的问题
  7. 云存储市场上演“新三国演义”
  8. PPM 金字塔池化模块 - PSPNet
  9. C# PPT/PDF文件转图片,图片转PPT
  10. python常用快捷键mac_Mac PHPStorm 常用快捷键,常用设置
  11. SAP培训行业权威评测---51sap培训评测网(www.51sap.net)
  12. AABB和OBB包围盒简介
  13. mmdetection中使用训练好的模型单张图片推理并保存到文件夹
  14. 联想微型计算机内存条安装,笔记本怎么加内存条,详细教您联想笔记本怎么加内存条...
  15. HUAWEI 擎云L420 折腾记 (搭建arm gcc、openocd 雅特力 MCU开发环境)
  16. UG6.0工厂橡胶模硅胶模设计3D档+PM编程刀路+资料
  17. c++打印心形_打印心形
  18. AppCode 2022Improves compatibility
  19. 第38篇:和骏君清华大学聊区块链
  20. 在量化交易方面,美国究竟比中国领先多久?

热门文章

  1. java集合——映射表+专用集合映射表类
  2. java集合——队列和双端队列+优先级队列
  3. poj doubles_余数运算符在Java中用于Doubles
  4. java uuid_Java UUID
  5. aws 性能_AWS上的应用程序自动扩展–选项和对性能的影响
  6. spark在服务器运行示例_创建示例HTTPS服务器以获取乐趣和收益
  7. js中使用camel框架_使用Fabric8在Kubernetes中使用Camel和CDI
  8. bean注入属性_摆脱困境:将属性值注入配置Bean
  9. jvm jinfo 参数_jinfo:JVM运行时配置的命令行浏览
  10. 成为Java流大师–第4部分:数据库流