永不再构建身份验证 –喜欢构建用户管理? 使用Okta,您可以在几分钟内为您的应用程序添加社交登录,多因素身份验证和OpenID Connect支持。 立即创建一个免费的开发者帐户。
在构建Java应用程序时,用户管理是至关重要的考虑因素。 应用程序和API通常会根据分配给用户的角色来划分对应用程序不同部分的访问权限-这是基于角色的访问控制(RBAC)。
这就是Okta的用处– Okta通过管理组内的角色(用户可以属于一个或多个组)来简化此过程。 借助Okta的Spring Security集成,该过程通过使用将组映射到特定角色并允许或拒绝访问的通用注释而变得自动化。 这是使用常见的Spring Security注释完成的,我们在下面对此进行了概述。
为了在实践中展示这一点,我在下面整理了一个演示,演示了一个简单但常见的场景。 在我们的示例中,我们将查看未受保护的页面,只有经过身份验证的用户才能访问的页面以及要求用户在访问它们之前必须具有额外授权级别的页面的混合。
我们在这里添加了我们引用的所有代码。
如果您已经打算开始使用Java应用程序的内置RBAC并开始使用它,只需在此处将Okta租户连接到Spring Boot应用程序即可。
如有任何疑问,请在此处联系Okta的开发支持团队。
第一步:配置您的Okta帐户
第一步是设置Okta租户。 这样,您可以启动我们创建的示例应用程序,并在实际中看到它。 在此处注册开发者帐户,然后按照以下步骤操作:
- 创建一个
admins
组 - 创建一个
users
组 - 创建属于
users
组的users
- 创建属于两个组的第二个用户
- 创建一个OpenID Connect(OIDC)应用程序
- 将两个组添加到应用程序
- 配置默认的授权服务器以在访问令牌中包括组成员身份
让我们来看一下它是什么样的:
设置组
在您的Okta管理员仪表板菜单中,找到“ Users
,然后单击“ Groups
。 单击Add Group
然后在Name
字段中输入admins
,然后添加组描述,例如:管理员。 单击Add Group
以完成此步骤。
按照相同的过程添加users
组。
设置用户
再次,在Okta Admin仪表板中导航至Users
,但这一次单击People
。 单击Add User
然后在表单中填写用户信息。 (将真实的电子邮件地址用作主电子邮件地址或辅助电子邮件地址,以及您可以访问的电子邮件地址,以便以后可以验证电子邮件。)在“ Groups
字段中,将此用户添加到您先前创建的users
组中。 确保已单击Send user activation email now
复选框,然后单击“保存并添加另一个”。
我们重复上述步骤,只是这一次,第二用户添加到两个 users
和admins
组。
跳到您的电子邮件并验证这些电子邮件地址。 单击两个用户的链接以激活它们。
创建一个OIDC应用程序
现在是时候设置您的身份验证层了。
在Okta Admin仪表盘中,单击菜单中的Applications
,然后单击Add Application
。
选择Web
,然后单击Next
。
当提示您填写表格时,请使用以下值:
| Field | Value |
| ------------------- | ------------------------------ |
| Name | Fun with Spring Security Roles |
| Base URIs | http://localhost:8080/ |
| Login redirect URIs | http://localhost:8080/ |
| Group assignments | `admins` and `users` |
| Grant type allowed | Check: `Implicit` |
准备就绪后,单击“完成”,您将在下面看到结果页面:
指定的URI是Spring Boot的默认值。 您以后可以轻松更改它们。
继续进行下一步之前,向下滚动并记下Client ID
。 您将需要它来配置Spring Boot应用程序。
设置授权服务器
接下来,在Okta管理员仪表板菜单中查找API
,然后单击Authorization Servers
以启动以下命令:
记下Issuer URI
。 稍后您还将需要此配置Spring Boot应用程序。
单击default
然后选择“ Claims
选项卡。 单击Add Claim
然后按如下所示填写字段:
| Field | Value |
| --------------------- | ------------ |
| Name | groups |
| Include in token type | Access Token |
| Value type | Groups |
| Filter | Regex .* |
| Include in | Any scope |
单击Create
以完成此步骤。
创建此声明可确保在用户进行身份验证时将组成员身份信息包括在访问令牌中。 这一步对于了解Spring Security的角色和权限机制至关重要。
第二步:配置您的Spring Boot应用
首先,在此链接处克隆。
在首选的IDE或编辑器中打开项目。 下面的截图来自这里。
复制application.yml.sample
文件并将其命名为application.yml
还记得我们标记要保存的那些值吗? 使用这些更新您的信息。 这是我们的示例:
| Name | Value |
| ----------- | ------------------------------------------------- |
| baseUrl | https://dev-237330.oktapreview.com |
| issuer | https://dev-237330.oktapreview.com/oauth2/default |
| audience | api://default |
| clientId | 0oacdldhkydGGruON0h7 |
| rolesClaim | groups |
| redirectUri | http://localhost:8080/ |
在进入代码之前,让我们看看实际的应用程序。
从命令行运行此命令以开始操作:
mvn spring-boot:run
查看实际应用
导航到主页,然后单击“ Login
。
要登录,请使用属于您在第一步中创建的“ Users
组的用户的凭据。 从那里,您将看到该应用程序显示您的用户信息,并在其下方显示一行按钮。
这些与应用程序内的访问权限相对应。 单击“ Users Only
时, users
组的成员将能够看到该页面。 对于admins
组的用户,情况相同,单击“ Admins Only
时可以看到该页面。
让我们看看它是如何工作的。
单击Users Only
。 您会看到一个页面,显示您是users
组的成员。
单击上Back
,然后单击Admins Only
。 这次,您将获得403 Unauthorized
页面,因为您不是admins
组的成员。
单击Logout
并再次登录,但是这次是属于两个组的用户(您在步骤1中创建的第二个用户)。 单击Admins Only
。
这次,您将看到您同时属于admins
和users
组。
很简单! 现在,让我们跳入代码...
第三步:Spring安全代码审查
本节概述Okta组如何链接到Spring Security角色。
该演示应用程序使用以下内容:
- Spring靴
- Spring安全
- Spring安全OAuth2
- Okta Spring安全入门
- 胸腺模板
- Spring安全4的Thymeleaf Extras
- Okta登录小部件
我们依赖okta-spring-security-starter
(来自pom.xml)。 这就是幕后魔术的发生方式:
...
<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-security-starter</artifactId><version>0.1.0</version>
</dependency>
...
让我们从Javascript Okta登录小部件开始,看看它如何将客户端连接到Spring Boot。
设置Okta登录小部件
这是我们在`login.html` Thymeleaf模板中设置Okta登录小部件的方式:
$( document ).ready(function() {var data = {baseUrl: [[${appProperties.baseUrl}]],clientId: [[${appProperties.clientId}]],redirectUri: [[${appProperties.redirectUri}]],authParams: {issuer: [[${appProperties.issuer}]],responseType: ['token']}};window.oktaSignIn = new OktaSignIn(data);// Check if we already have an access tokenvar token = oktaSignIn.tokenManager.get('token');if (token) {window.location.href = "/authenticated";} else {renderWidget();}
});
在第3-8行中,您将看到我们已经嵌入了所有设置以作为嵌入式Thymeleaf Template变量连接到Okta租户。 这些值从Spring Boot控制器作为模型的一部分传入。 这样,您只需指定一次这些设置-现在服务器端和客户端都可以确保它们。 我们将在下面详细说明如何管理这些设置(它们全部来自application.yml
文件)。
在配置并实例化Okta登录小部件之后,下一步是检查用户是否已登录。然后执行以下两个操作之一。 如果有,我们将其发送到/authenticated
页面。 如果还没有,我们将渲染小部件,这将为用户提供登录的机会。
这是renderWidget
函数:
function renderWidget() {oktaSignIn.renderEl({el: '#okta-login-container'},function (response) {// check if successif (response.status === 'SUCCESS') {// for our example we have the id token and the access tokenoktaSignIn.tokenManager.add('token', response[0]);if (!document.location.protocol.startsWith('https')) {console.log('WARNING: You are about to pass a bearer token in a cookie over an insecure\n' +'connection. This should *NEVER* be done in a production environment per\n' +'https://tools.ietf.org/html/rfc6750');}document.cookie = 'access_token=' + oktaSignIn.tokenManager.get('token').accessToken;document.location.href = "/authenticated";}},function (err) {// handle any errorsconsole.log(err);});
}
在窗口小部件上呈现了窗口小部件之后,内部逻辑将根据用户登录时的设置来接管。在这种情况下,您正在使用此流,并且仅获取由配置的responseType
参数指定的访问令牌。 。
成功登录后,您将输入带有response
对象的回调函数。 响应对象具有您的访问令牌(在本例中为您用户的访问令牌)。
第19行使用访问令牌设置cookie,第20行将(现在已认证的)用户发送到/authenticated
端点。
此时,Spring Security可以识别经过身份验证的用户。
在介绍Spring Security角色之前,让我们看一下Spring Security如何处理访问令牌。
Spring Security令牌提取器
默认情况下,Spring Security OAuth 2.0插件处理进入Authorization
标头中的访问令牌作为承载令牌。 这对于为客户端(例如Angular客户端)创建RESTful响应的应用程序很好。
对于此示例,我将Javascript的体系结构和数量保持在最低限度,因此我希望进行完整的页面转换。 这有点老套了,但是它使示例代码紧凑而紧凑。
为了使Spring Security能够识别用户已通过身份验证,我们需要它能够处理进入cookie的令牌。
幸运的是,通过设置TokenExtractor
,Spring Security使覆盖默认行为变得非常容易。 这是从OktaSpringSecurityRolesExampleApplication
实现此目的的代码:
@Bean
protected ResourceServerConfigurerAdapter resourceServerConfigurerAdapter() {return new ResourceServerConfigurerAdapter() {...@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenExtractor(new TokenExtractor() {@Overridepublic Authentication extract(HttpServletRequest request) {String tokenValue = findCookie(ACCESS_TOKEN_COOKIE_NAME, request.getCookies());if (tokenValue == null) { return null; }return new PreAuthenticatedAuthenticationToken(tokenValue, "");}...});}};
}
这样做是从传入请求的Cookie列表中提取访问令牌(如果可以)。 然后,将自动完成解析和验证。 瞧!
建立基于角色的访问控制
在应用程序设置中,您定义打开哪些路径。 所有其他路径至少需要经过身份验证的会话。
这是OktaSpringSecurityRolesExampleApplication
的另一个摘录:
@Bean
protected ResourceServerConfigurerAdapter resourceServerConfigurerAdapter() {return new ResourceServerConfigurerAdapter() {@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/", "/login", "/images/**").permitAll().and().exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);}...};
}
在这种情况下,您要告诉Spring Security允许任何未经身份验证的用户访问主页( /
),登录页面( /login
)以及任何来自静态图像文件夹的内容。 这意味着默认情况下会自动限制所有其他路径。
此时,您还定义了一个自定义的访问拒绝处理程序。
@Controller
public class SecureController {@Autowiredprotected AppProperties appProperties;@RequestMapping("/authenticated")public String authenticated(Model model) {model.addAttribute("appProperties", appProperties);return "authenticated";}@RequestMapping("/users")@PreAuthorize("hasAuthority('users')")public String users() {return "roles";}@RequestMapping("/admins")@PreAuthorize("hasAuthority('admins')")public String admins() {return "roles";}@RequestMapping("/403")public String error403() {return "403";}
}
在此控制器中,我们定义了四个路径,所有这些路径都至少需要经过身份验证的用户。
真正的价值来自/users
和/admins
路径。 请注意,它们都具有@PreAuthorize
批注。 这意味着, 在随后的表达必须在方法之前应满足甚至会被输入。 hasAuthority
函数确认经过身份验证的用户是否属于那些角色。 在此示例中,这些自动映射到我们之前创建的Okta组,这就是为什么在Okta的访问令牌中包含groups
声明很重要的原因。
尽管这涉及到一些设置,但是现在您仅需一行代码即可实现基于角色的访问控制!
端到端配置
在客户端,我们从应用程序本身提供了一组Thymeleaf模板形式HTML页面。 因此,有一个单一的来源来提供客户端和服务器所需的配置值是有意义的。
使用Spring的@Component
和@ConfigurationProperties
批注很容易。
这是AppProperties
类:
@Component
@ConfigurationProperties("okta.oauth")
public class AppProperties {private String issuer;private String audience;private String clientId;private String rolesClaim;private String baseUrl;private String redirectUri;... getters and setters ...
}
@ConfigurationProperties
告诉Spring从application.yml
文件中okta.oauth
属于okta.oauth
密钥的所有属性。
@Component
注释使Spring实例化此Object并使它可用于其他地方的自动装配。
看一下HomeController
这段代码:
@Controller
public class HomeController {@Autowiredprotected AppProperties appProperties;...@RequestMapping("/login")public String login(Model model) {model.addAttribute("appProperties", appProperties);return "login";}
}
在单击/login
端点时返回login
视图之前,将AppProperties
对象(在第4和5行自动AppProperties
)添加到模型中。
这就是使它可用于Thymeleaf模板的原因:
<script th:inline="javascript">/*<![CDATA[*/$( document ).ready(function() {var data = {baseUrl: [[${appProperties.baseUrl}]],clientId: [[${appProperties.clientId}]],redirectUri: [[${appProperties.redirectUri}]],authParams: {issuer: [[${appProperties.issuer}]],responseType: ['token']}};window.oktaSignIn = new OktaSignIn(data);...});.../*]]>*/
</script>
开始编码!
就是这样! 试一试,让我知道它的进展! 我在Twitter上。
虽然我已经概述了将Okta的Groups机制与Spring Security的基于角色的访问控制结合使用的好处,但Okta的Java开发团队仍在努力开发我们的下一代SDK和集成。 请随时注意新的Okta Java Spring Boot Integration的即将发布的版本,该版本将支持其他OIDC工作流程,包括code
以及托管的,可配置的登录和注册视图。
该帖子已从此处改编。
永不再构建身份验证 –喜欢构建用户管理? 使用Okta,您可以在几分钟内为您的应用程序添加社交登录,多因素身份验证和OpenID Connect支持。 立即创建一个免费的开发者帐户。
翻译自: https://www.javacodegeeks.com/2017/11/secure-java-app-spring-security-thymeleaf-okta.html