前言
本文仅代表作者的个人观点;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有26篇。
一、Http几种认证方式
在Gartner定义的“第三平台”盛行的年代,html5大行其道。所以http方式访问的应用很多。因此,谈到应用的安全,我们先要了解http的几种认证方式。
http的认证方式大致有以下几种:
Http Basic Authentication
HTTP Basic Authentication,是通过用户名密码方式认证的。登录浏览器,输入地址,提供输入用户名和密码,验证通过,可以登录界面。
在这种方式,浏览器会把用户名和密码通过BASE64编码在HTTP HEAD 里面
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
服务器端解析之后做身份验证,并给客户端返回
WWW-Authenticate: Basic realm="User Visible Realm"
客户端每次请求都会携带用户名密码,需要通过HTTPs来保证安全。另外客户端需要缓存用户名和密码,以保证不必每次请求都要用户重新输入用户名和密码,通常浏览器会在本地保存10分钟左右的时间,超过之后需要用户再次输入用户名密码。
Http Digest Authentication Digest
Digest authentication 是对前面 Basic access authentication 的升级版本,它不再使用Base64的用户名/密码而是对于用户名密码做哈希取得一个摘要
字符串再传给服务器,这样在传输的过程中不会暴露用户名和密码。
目前为止我们在登陆网页时看到的登陆页面基本都是基于Form-based Authentication,是最流行的身份验证方式。
当用户访问一个未授权网页的时候,服务器会返回一个登陆页面,用户输入用户名/密码并点击提交按钮,浏览器把表单信息发送给服务器,服务器验证之后创建Session,并把Cookie返回给浏览器。在下次请求的时候,浏览器会把Cookie附加在每个请求的HEAD里面,服务器通过验证Cookie来校验接下来的请求。由于表单信息是明文传输的,所以需要额外的措施来保证安全(比如:HTTPS)。
这种授权方式源于OAuth,现在在单页面应用(SPA)中逐渐流行起来(普遍应用在移动App中)。它的大概过程和基于Form/Cookie的授权方式一致,客户端 发送用户名/密码给服务器,服务器返回一个Token(token包含一个过期时间)给客户端
{ "refresh_token":"xxxx"
"token": "xxxxx"}
客户端拿到Token之后被缓存在本地,以后每次请求的时候在HEAD里面带上Token,这样服务器便可以验证客户端, 如果Token过期客户端可以通过RefreshToken再次获取新的Token。。
Authorization: xxxx
通常在基于Cookie的身份验证中,Cookie存储的是SessionId,服务器端需要通过Session来维护会话的状态。但是在SPA或者移动类的REST应用中,状态在本地维护一般使用token来实现无状态的服务器,简化服务器端的逻辑。
二、Java EE应用安全性
谈到安全,其实是两方面的内容:认证和授权。
定义哪些用户有权访问应用程序称为身份验证,
而在应用程序中为这些用户定义权限称为授权。
理想情况下,在为各种应用程序组件定义访问限制时,用户仅限于每个用户所需的最小访问量。要在应用程序中自定义授权,对用户(表示个人)或角色应用限制,该用户指的是已定义的用户组。
例如,一个在线书店Web应用程序,客户在线购买书籍,商店所有者管理库存。用户shadowman是访问该站点的客户,并且具有客户角色。用户名为redhat的站点管理员具有admin角色。服务器对用户shadowman和redhat进行身份验证,以确保每个用户都匹配其密码。经过身份验证后,EJB方法将被注释为限制对单个用户角色的访问。由于不允许客户管理商店的库存,因此具有角色客户的用户无法调用管理库存的方法,而具有角色admin的用户可以进行库存更改。
Java身份验证和授权服务(JAAS)是一种安全API,用于在Java应用程序(JSR-196)中实现用户身份验证和授权。
JAAS大致有两种实现方式:
1. 声明性的安全declarative security
2. 编程性安全 programmatic security
JAAS在JBoss EAP 提供声明性的、基于角色的安全性。 声明性安全性通过使用 EJB container来管理安全性,将安全性问题与应用程序代码分开。EJB container基于应用程序代码中的注释和XML描述符提供授权系统,以保护资源。 这种方法与编程安全性形成对比,编程安全性要求每个应用程序都包含管理安全性的代码。
二、声明性安全
声明性安全,要求开发人员和管理员利用注释和部署描述符,来定义应用程序的安全行为。 例如,EJB可以仅使用注释来基于用户的角色来限制应用程序的各个方面。 它不需要应用程序来管理安全上下文。 要管理安全性方面(如管理身份验证和授权),需要部署描述符,负责指示应用程序服务器如何部署应用程序以及服务器如何保护应用程序。 这是在应用程序的web.xml中设置的,或者在使用Red Hat JBoss EAP进行开发时,在jboss-web.xml中设置。
开发人员使用web.xml文件来定义应保护应用程序中的哪些资源,如何保护它们以及用于验证凭据的数据。 以下是定义BASIC身份验证(本文第一节提到的http几种认证方式之一)的web.xml片段:
1.安全性约束适用的资源。 “/ *”表示所有资源都是安全的。
2.有权访问资源的角色。 在这种情况下,所有角色都可以访问该应用程序。
3.应用程序用于访问用户凭据的方法。 一旦访问应用程序,BASIC就会在弹出窗口中提示用户。
4.存储用户凭据信息的域的名称。
jboss-web.xml文件添加了其他JBoss特定的描述,例如EAP如何处理应用程序的身份验证和授权。 在许多情况下,此文件用于定义安全域,该域是一组JAAS声明性安全配置。
使用部署描述符来定义安全性方面可能会有所帮助,但它们也会受到严重限制,尤其是在具有超过最基本安全要求的任何应用程序中。 直接放在EJB应用程序代码中的注释,提供了更灵活和可自定义的安全方法。 此方法对于保护REST API的方法或将某些角色限制为仅使用应用程序中的某些方法调用很有用。,可用于保护EJB的注释:
@SecurityDomain:此批注位于类的开头,按名称定义用于EJB的安全域。
@DeclareRoles:位于类的开头,此批注定义了在类中测试权限的角色。如果未使用此注释,则会根据@RolesAllowed注释的存在来检查角色。
@RolesAllowed:位于类的开头或方法标题之前,此批注定义了允许访问方法的一个或多个角色的列表。如果放在类标题之前,则没有注释的类中的方法默认为此批注。
@PermitAll:位于类的开头或方法标题之前,此批注指定允许所有角色访问方法。
@DenyAll:位于类的开头或方法标题之前,此批注指定不允许任何角色访问方法。
@RunAs:位于类的开头或方法头之前,此批注指定运行方法时使用的角色。当EJB调用另一个EJB并且需要为另一个EJB中的安全性限制承担新角色时,此注释很有用。
1 HelloWorldEJB类默认将其所有方法限制为仅供admin和qa用户使用。
2 该类正在利用other
安全域。 在这种情况下,此安全域使用存储角色信息的属性文件。
3 HelloWorld方法适用于所有角色,而不仅仅是admin和qa。
4 GoodbyeAdmin方法仅适用于以角色admin身份验证的用户。
5 GoodbyeSecure方法仅适用于admin和qa,因为该方法默认为在类级别定义的RolesAllowed。
三、编程性安全
编程式安全控制可以通过EJBContext或HttpServletRequest实现的,主要使用该接口的如下3个方法:
以下示例演示如何使用EJB Context标识用户和用户的角色:
@Stateless
public class HelloWorldEJB implements HW {
@Resource
EJBContext context;
public String HelloWorld() {
if (context.isCallerInRole("admin")) {
return "Hello " + context.getCallerPrincipal().getName();
} else {
return "Unauthorized user.";
}
}
}
在此示例中,HelloWorld()方法使用EJBContext检查调用方法的用户是否属于admin角色。 如果用户确实属于此角色,则会返回带有经过身份验证的用户的用户名的响应。
除了使用EJBContext之外,HttpServletRequest接口还提供了以编程方式管理用户身份验证的方法。 以下方法可用于使用HttpServletRequest接口对用户进行身份验证:
四、基于声明式安全:在JBoss EAP中配置安全域
使用app server,如EAP,可简化开发人员和应用程序管理员的安全配置和实施。 EAP和其他应用程序服务器提供实用程序和预定义的默认配置,以帮助管理身份验 EAP管理安全领域中的用户安全信息。 默认情况下,EAP定义ApplicationRealm,它使用以下文件分别存储用户及其角色:
虽然可以向EAP添加更多安全领域。 以下是ApplicationRealm的默认配置:
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
请注意,域有<authentication>和<authorization>的标记。 <authentication>标记定义用户属性文件的路径。 在这种情况下,该文件是EAP服务器配置目录中的application-users.properties。 此文件将用户名和密码存储为键值对,例如:
<username>=<password>
<authorization>标记定义角色属性文件的路径。 在这种情况下,该文件是application-roles.properties,它位于EAP服务器配置目录中。 此文件使用以下语法将用户和角色存储为键值对:
<role>=<user1>,<user2>...
五、登录模块
EAP包括几个内置登录模块,开发人员可以使用这些模块在安全域中进行身份验证。这些登录模块包括从关系数据库,LDAP服务器或平面文件中读取用户信息的功能。也可以根据应用程序的安全要求构建自定义模块。
用户认证的方法在安全域中定义。默认情况下,EAP使用以下配置定义其他安全域:
<security-domain name="other" cache-type="default">
<authentication>
...
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
RealmDirect登录模块,在进行身份验证和授权决策时使用应用程序领域。 如果未指定域,则模块使用ApplicationRealm,因此使用用户和角色属性文件进行身份验证和授权。
要在应用程序中启用其他安全域,请将以下标记添加到应用程序的src/main/webapp / WEB-INF/jboss-web.xml:
<security-domain>other</security-domain>
要完成身份验证,需要在应用程序中配置WEB-INF/web.xml文件以定义<login-config>。 以下示例将<login-config>定义为使用ApplicationRealm进行BASIC身份验证。 使用此配置后,将在用户访问服务器资源时提示用户输入凭据,并且服务器将根据ApplicationRealm验证凭据。
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
</web-app>
六、UsersRoles登录模块
UsersRoles登录模块是一个简单的模块,可用于测试应用程序的一些基本安全功能。 该模块为开发人员提供了一种快速验证用户身份并验证是否正确配置了授权限制的方法。 与依赖ApplicationRealm一样,其他安全域也是如此,UsersRoles模块使用属性文件来存储用户凭据和角色数据。
以下是UsersRoles登录模块的示例:
1安全域的名称。 此名称在jboss-web.xml文件中引用。
2用于定义正在使用的登录模块的代码。 在这种情况下,正在配置UsersRoles登录模块。
3用于定义登录模块行为的标志。 required表示模块需要身份验证才能成功。
4用于定义将所有用户和密码存储为键值对的文件名的属性。
5用于定义将所有用户角色存储为键值对的文件名的属性。
七、数据库登录模块
在生产环境中,查看存储在本地存储的属性文件中的用户凭据和角色信息非常罕见。 这些模块和技术主要用于测试目的。 用于管理用户凭证的本地属性文件比实用解决方案更实用的一种是将信息存储在数据库中。 使用数据库而不是文件来存储用户信息有很多好处。 数据库可以在多个应用程序服务器之间轻松共享,它们包括强大的数据安全性和备份解决方案,并且它们对于大型数据集非常有效。 如果应用程序使用数据库登录模块,则应用程序用户将与用户关联的角色一起存储在数据库中。
1用于定义使用哪个登录模块的代码。 在这种情况下,正在配置数据库登录模块。
2用于定义用于访问数据源的JNDI名称的属性。 请注意,必须已配置此数据源。
3用于定义用于获取给定用户的密码的查询的属性。 此查询取决于数据库的配置方式。
4用于定义用于获取给定用户角色的查询的属性。 此查询取决于数据库的配置方式。
七、在JBoss EAP中配置安全域:基于声明的方式
通过maven导入一个已经存在的maven项目:
然后启动EAP:
在终端窗口中,运行以下脚本以在正在运行的EAP服务器中创建安全域。
首先查看脚本内容:
执行脚本:
接下来,查看EAP的启动文件(standalone-full.xml),能够找到刚才脚本增加的内容:
更新jboss-web.xml文件以使用新的安全域。
通过展开JBDS左侧窗格中Project Explorer选项卡中的security-domain项打开jboss-web.xml文件,然后单击security-domain→src / main / webapp→WEB-INF并展开它。 双击jboss-web.xml文件。
更新jboss-web.xml文件以使用名为userroles的新安全域:
增加内容如下:
更新web.xml文件以使用BASIC身份验证并限制对应用程序的admin.jsf的访问。
通过展开JBDS左窗格中Project Explorer选项卡中的security-domain项打开web.xml文件,然后单击security-domain→src/main/webapp→WEB-INF并展开它。 双击web.xml文件。
第一个安全约束是指index.html。 这是Web应用程序的主页面。 将以下<auth-constraint>添加到index.html安全性约束,以将此资源的访问权限仅限于具有guest和admin角色的用户。
更新第二个安全性约束,将admin.jsf页面的访问权限仅限于具有admin角色的用户。 添加新的auth-constraint并更新url-pattern。
将login-config的auth-method设置为BASIC认证:
使用角色guest和hello-users.properties和hello-roles.properties中具有admin角色的用户创建用户以访问Web应用程序。
在新的终端窗口中,导航到/home/student/JB183/labs/security-domain目录并创建一个名为hello-users.properties的新文件:
使用终端窗口中的以下命令部署安全域应用程序:
通过浏览器到http:// localhost:8080 / security-domain,将安全域测试为客户和所有者用户。
在工作站VM上的Firefox中,导航到http://localhost:8080/security-domain。
出现提示时,输入以下凭据以客户用户身份登录:
用户名:customer
密码:redhat1!
认证通过,登录界面:
点击admin Page,直接显示禁止:
再次登录,使用admin用户:
点击admin page:
可以看到相对应的页面:
参考文献:
1.红帽技术文档
2. https://www.jianshu.com/p/eaf9197abb6b
3.https://blog.csdn.net/Crystalbruce/article/details/7385401
魏新宇