“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
在本文中,您将研究使用Spring Boot 2.1实现登录功能的各种选项。 您将从最简单的基本身份验证开始,除了内部后端工具外,您可能永远不想使用它,然后转到简单的基于表单的身份验证页面。 接下来,您将通过覆盖一些默认模板和控制器来自定义默认的,自动生成的表单。 最后,您将继续使用OAuth 2.0添加单一登录。 您将看到如何允许用户使用Github和Okta登录。
本教程的假设是非常基本的。 我假设您已经对Spring和Spring Boot有了基本的了解,但是不一定对Spring Security或Spring的各种安全功能有深入的了解。 您将需要安装git
,并且gradle
会很好,但是您可以对所有gradle
命令使用Gradle包装器,因此这并不是绝对必要的。
继续克隆我为本教程创建的存储库 :
git clone https://github.com/oktadeveloper/okta-spring-boot-login-options-example.git
该存储库包含五个子目录:
-
basic-auth
-
form-auth
-
custom-form-auth
-
oauth-start
-
oauth-okta-github
-
oauth-okta-starter
这些对应于本教程的四个部分(最后三个部分是OAuth部分,其中三个部分)。 除了oauth-start
,这些都是功能齐全的应用程序。 在本教程中,您将遍历代码的含义以及如何构建它们。
在Spring Boot中构建HTTP基本登录
基本身份验证是基本的。 这是直接内置在HTTP协议中的简单方案。 它来自田园时代,大量的数据和金钱流过互联网的各种管道。 因此,它确实不是非常安全。 根据规范,密码和用户名使用HTTP authorization
标头中的Base64进行编码。 因为Base64可能也是纯文本格式,所以如果您要对任何内容使用基本身份验证,请确保始终使用HTTPS / SSL,因为每个请求都将发送身份验证凭据。
使用您喜欢的IDE或编辑器打开basic-auth
Spring Boot项目。
您要签出的第一件事是build.gradle
文件。 复制如下。 我不会详细介绍所有这些内容,但是如果您还不熟悉,我想让您了解这里发生的一些事情。
plugins { id 'org.springframework.boot' version '2.1.4.RELEASE' id 'java'
} apply plugin: 'io.spring.dependency-management' group = 'com.okta.springsecurityauth'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8' repositories { mavenCentral()
} dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test'
}
有两个Spring插件被添加到构建中: org.springframework.boot
和io.spring.dependency-management
。 请注意,版本设置为2.1.4.RELEASE
。 过去,Spring的真正麻烦之一就是依赖管理。 Spring是一个庞大的库集合,当您引入一个库时,您需要引入其他具有兼容版本的库。 当您更新一个时,通常这会导致其他人失去兼容性。 过去解决此问题可能会使您陷入所谓的“依赖地狱”。
幸运的是,Spring使事情变得更加轻松。 查看我们的两个Spring依赖项:
dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web'
...
}
请注意,它们没有版本号。 这些基本上是功能集(在这种情况下为安全性和Web)的大型元依赖项,并且Spring插件根据此行中设置的版本为您管理所有子依赖项:
id 'org.springframework.boot' version '2.1.4.RELEASE'
该build.gradle
文件的全部执行摘要将加载Spring Boot版本2.1.4,并将添加Web和安全功能模块。 Spring的所有可用启动程序的列表可以在其docs中找到 。
除了gradle.file
,实际上只有三个其他感兴趣的文件,所有Java文件。 在src/main/java/com/okta/springsecurityauth
查看,您将看到它们:
-
Application.java
(自动魔术化整个Spring Boot框架的主类) -
SecurityConfiguration.java
(配置安全性选项) -
WebController.java
(非常基本的HTTP请求控制器)
Application.java
是Application.java
的入口点。 在这种情况下,而且在许多情况下,这非常简单。 最重要的是@SpringBootApplication
批注,它告诉您的Spring依赖项引导整个Spring Boot框架。 当然,还有main()
方法,这是Spring加载和运行Application
类的地方。
package com.okta.springsecurityauth; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
WebController.java
足够聪明,它是您的Web请求控制器。 它定义了请求端点并确定了响应。
package com.okta.springsecurityauth; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
public class WebController { @RequestMapping("/") @ResponseBody public String index() { return "That's pretty basic!"; } }
在这种情况下,Web控制器将返回一个简单的字符串,而不是路由到模板文件,这将在稍后介绍。 @ResponseBody
批注允许该方法直接返回字符串。
在本教程中, SecurityConfiguration.java
是执行操作的地方。 这是Spring Boot配置为使用基本身份验证的地方。 您还可以在这里配置硬编码的默认用户名和密码(显然,这不是我在生产中要做的,但是非常适合教程)。
package com.okta.springsecurityauth; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user") .password("{noop}pass") // Spring Security 5 requires specifying the password storage format .roles("USER"); } }
如果您查看configure()
方法,您将看到http
对象及其流畅接口用于告诉spring认证所有请求并使用HTTP基本认证。 这是相当琐碎的,但是稍后您将在此界面中看到大量功能。
configure()
方法主要是本教程的一个技巧,目的是在内存身份验证管理器中创建用户。 您正在使用用户名user
和password pass
创建一个用户。 用户已分配了USER
角色。
而已!
打开终端,并确保您位于basic-auth
项目的根目录中。 使用以下命令运行项目:
./gradlew bootRun
等待Spring Boot应用完成加载。
导航到http://localhost:8080
。
您会看到一个登录窗口。
使用user
登录并作为凭据pass
。
系统将显示一个成功屏幕,显示“这很简单!”
配置基于Spring Boot Form的登录
使用基本身份验证时,登录表单实际上是由浏览器而不是由应用程序生成的。 它显示为无样式的弹出窗口。 这不是很好的流程,也不是非常专业的外观。
只需对代码进行一些很小的更改,您就可以让Spring Boot自动生成外观更专业的登录表单。 可以在GitHub存储库的form-auth
子目录中找到完整的代码,或者如果仍然打开,则可以仅对basic-auth
项目进行更改。
您想要将SecurityConfiguration.java
文件的configure()
方法更改为如下所示:
@Override
public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic();
}
唯一的不同是添加了.and().formLogin()
。 除此之外,这两个项目是相同的。
使用./gradlew bootRun
再次运行它。
导航到http://localhost:8080
(您可能需要打开一个隐身窗口以触发重新认证)。
这次,您将看到Spring为您生成的登录页面。
自定义Spring Boot登录表单
我们日益复杂的Spring Boot身份验证过程的下一步是自定义登录表单。 Spring使这个超级容易。 但是,有许多新文件,并且对代码的更改比上次更多,因此我建议打开在回购的custom-form-auth
文件夹下找到的项目。
在build.gradle
文件中,有一个新的依赖项。 这引入了Thymeleaf模板引擎,这是与Spring Boot一起使用的默认Web模板引擎。
dependencies { ...implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' ...
}
现在, SecurityConfiguration.java
文件如下所示(为简洁起见,省略了一些部分):
package com.okta.springsecurityauth; ...@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers( "/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") .failureUrl("/login-error.html") .permitAll(); }...}
请注意, .httpBasic()
已消失, .formLogin()
具有一些新选项。 在这里,您将设置一个登录页面和一个登录失败页面,并确保它们是公开可用的。
还要注意这一行:
.antMatchers( "/public/**").permitAll()
在这里,您无需进行身份验证即可使类路径根目录上的public
目录可用。 在src/main/resources/static
有一个public
目录。 启动该应用程序时, src/main/resources/static
目录下的所有内容都将复制到类路径,因此public
目录最终位于类路径根目录下,并且其中的所有文件无需身份验证即可使用。 这非常适合图像,JavaScript文件和CSS文件之类的东西。
WebController.java
也有一些新的端点:
package com.okta.springsecurityauth; ...@Controller
public class WebController { @RequestMapping("/") @ResponseBody public String index() { return "You made it!"; } // Login form @RequestMapping("/login.html") public String login() { return "login.html"; } // Login form with error @RequestMapping("/login-error.html") public String loginError(Model model) { model.addAttribute("loginError", true); return "login.html"; } }
注意,虽然index()
方法具有@RequestBody
批注,这意味着它将直接以字符串形式返回其请求主体,但login()
和loginError()
方法却没有。 相反,他们返回的是Spring Boot将呈现的Thymeleaf模板的名称。 默认情况下,这些模板位于src/main/resources/templates
。 另外,请注意, loginError()
有点路由技巧。 实际上,它只是返回login.html
模板,但在模型中注入了error属性。
src/main/resources/templates/login.html
是一个新文件。 这是登录模板文件。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head> <title>Login page</title> <link th:href="@{/public/style.css}" rel="stylesheet" />
</head>
<body>
<div id="container"> <h2>Login page</h2> <form th:action="@{/login.html}" method="post"> <label for="username">Username</label> <input type="text" id="username" name="username" autofocus="autofocus" /> <label for="password">Password</label> <input type="password" id="password" name="password" /> <input id="submit" type="submit" value="Log in" /> </form> <p th:if="${loginError}" class="error">Wrong user or password</p>
</div>
</body>
</html>
关于Thymeleaf的真实,深入的解释远远超出了本教程的范围。 如果愿意,请访问他们的网站进行深入研究。
还记得您将public
目录公开吗? 这行有一个非常基本的样式表。 通常,我将其包括在内是为了演示一种在模板文件中包含静态资源的方法。
<link th:href="@{/public/style.css}" rel="stylesheet" />
根据模型的loginError
属性有条件地呈现错误行,该属性由控制器的loginError()
方法注入。
<p th:if="${loginError}" class="error">Wrong user or password</p>
除此之外,这是一个非常简单的登录表单!
再次使用./gradlew bootRun
运行它。
您将看到样式化的定制登录表单:
使用user:pass
再次登录,您将看到我们的成功屏幕,显示“您成功了!”
使用GitHub和Single Sign-On的Spring Boot OAuth 2.0登录
到目前为止,您已经使用临时内存AuthenticationManager
在本地完成了所有AuthenticationManager
。 在生产中更常见的是,应用程序支持OAuth 2.0和OIDC(开放ID连接)。 OAuth 2.0是开放的授权标准。 OIDC建立在OAuth 2.0之上,并添加了身份验证,以实现更完整的身份管理协议。
在本教程中,您首先将看到如何使用GitHub添加单点登录(SSO)。 之后,您将看到如何使用软件即服务身份解决方案提供商Okta。
两者都非常简单,并且允许用户重用现有的身份提供者确实有好处。 强迫用户处理和管理数百个网站的单独的强密码是一个愚蠢的事情,最终会导致弱密码和在多个站点之间重复使用的密码(乘以其易受攻击性); 更不用说旧的“只是在便签上随意写,然后将其粘贴到显示器上”密码管理解决方案。
通过GitHub Single Sign-on简化您的Spring Boot App登录
现在是时候实现GitHub OAuth 2.0客户端了。
首先,您需要在GitHub上注册一个新的OAuth应用程序。 转到他们的网站并立即执行此操作。 填写如下所示的值。
请特别注意Authorization回调URL ,因为它需要为http://localhost:8080/login/oauth2/code/github
。
单击注册应用程序 。 请注意客户端ID和客户端密钥,因为您很快就会需要它们。
现在,回到Java。 在您的IDE中打开oauth-start
项目。
在build.gradle
文件中,注意几个新的依赖项:
...
dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.security:spring-security-oauth2-client' implementation 'org.springframework.security:spring-security-oauth2-jose' ...
}
安全性,Thymeleaf和网络启动程序仍然存在。 但是,有两个新的Spring启动器: oauth2-client
和oauth2-jose
。
oauth2-client
了实现OAuth 2.0客户端所需的库。 oauth2-jose
了一些用于签名和加密的通用库。 JOSE代表Java对象签名和加密。
OConfiguration登录的SecurityConfiguration.java
文件已更新:
package com.okta.spring.SpringBootOAuth;
...@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**").permitAll() .anyRequest().authenticated() .and() .oauth2Login(); }
}
需要注意的两件事:1)对/
和login
所有请求都是公共的,以及2) oauth2Login()
方法是导致Spring Boot配置OAuth 2.0客户端的原因。
如果这似乎太简单了 ,那是正确的。 一些配置已移至application.yml
文件。
打开src/main/resources/application.yml
文件:
spring: thymeleaf: cache: false security: oauth2: client: registration: github: client-id: << your GitHub client ID>> client-secret: << your GitHub client secret >>
您需要从上方将Client ID和Client Secret填写到该文件中。
Web路由和模板也已更改。 查看WebController.java
文件:
package com.okta.spring.SpringBootOAuth; ...@Controller
public class WebController { @RequestMapping("/securedPage") public String securedPage(Model model, @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, @AuthenticationPrincipal OAuth2User oauth2User) { model.addAttribute("userName", oauth2User.getName()); model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName()); model.addAttribute("userAttributes", oauth2User.getAttributes()); return "securedPage"; } @RequestMapping("/") public String index() { return "index"; } }
该控制器定义了两个端点,这些端点返回两个模板文件:
-
/
–>src/main/resources/templates/index.html
-
/securedPage
–>src/main/resources/templates/securedPage.html
在securedPage()
方法中,请注意如何使用依赖性注入来获取有关已认证用户的信息,以便可以将其注入模型中,然后将其传递给模板文件。
src/main/resources/templates/index.html
非常简单:
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <title>Home</title>
</head>
<body> <h1>Spring Security SSO</h1> <a href="securedPage">Login</a>
</body>
</html>
以及src/main/resources/templates/securedPage.html
模板文件:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head> <meta charset="UTF-8"> <title>Secured Page</title>
</head>
<body> <h1>Secured Page</h1> <div>User Name: <span th:text="${userName}"></span></div> <div>Client Name: <span th:text="${clientName}"></span></div> <div>User Attributes: <span th:text="${userAttributes}"></span></div>
</body>
</html>
现在您可以尝试了。 再次,确保您在项目根目录中,然后运行./gradlew bootRun
。
导航到http://localhost:8080
,然后单击“ 登录”链接。
如果您的浏览器绕过登录页面并直接进入/securedPage
端点,请打开一个隐身浏览器,然后重试。
首先,您需要授权Spring Boot Login应用程序使用您的Github帐户进行OAuth登录。
接下来,您将看到GitHub OAuth登录屏幕:
如果成功,您将看到一个简单的文本页面,该页面以粗体显示“安全页面”,并显示有关您的OAuth用户的信息。
很漂亮吧?
现在,您将添加第二个OAuth提供程序:Okta。
将使用Okta登录的OAuth 2.0登录到您的Spring Boot应用
首先,如果您还没有开发人员帐户,请访问developer.okta.com并注册一个免费的开发人员帐户。
拥有帐户后,要创建OAuth / OIDC应用,请打开Okta开发人员信息中心。 单击“ 应用程序”顶部菜单项,然后单击“ 添加应用程序” 。
选择Web作为平台,然后单击Next 。
为应用命名。 我将其命名为“ Spring Boot Login”,但是您可以随意命名。
将登录重定向URI更改为http://localhost:8080/login/oauth2/code/okta
。
其他默认值也可以。
单击完成 。
在下一页上记下客户端ID和客户端密钥 。
更新Okta的application.yml
文件:
spring: ...security: oauth2: client: registration: github: client-id: << Your GitHub Client ID >> client-secret: << Your GitHub Client Secret >> okta: client-id: << Your Okta Client ID >> client-secret: << Your Okta Client Secret >> client-name: Oktaprovider: okta: issuer-uri: https://{yourOktaDomain}/oauth2/default
您需要添加Okta注册和提供商条目,并确保填写您的Client ID , Client Secret和Okta发行者。
重新启动应用程序,转到http://localhost:8080
,然后单击“ 登录”链接。
这次,您将可以选择OAuth提供商。 该屏幕由Spring Boot自动生成。
单击Okta ,您将被带到Okta登录页面(通常有关注销和/或隐姓埋名的警告)。
成功登录,您将看到我们的成功页面。
至此,您已经将oauth-start
项目与oauth-github-okta
项目文件夹进行了更新,并且已经看到了将多个OAuth提供程序添加到Spring Boot应用程序是多么容易。
使用Okta的Spring Boot Starter简化OAuth 2.0登录
在本教程中,您要做的最后一件事是将Okta配置为允许新用户注册。 但是,在此之前,我想向您介绍Okta Spring Boot Starter。 该项目简化了Spring Boot和Okta的集成。 该项目的自述文件是获取更深入信息的好资源。
您将把Okta Spring Boot Starter集成到该项目中,并稍微简化配置。 为了简单起见,您还将删除GitHub OAuth集成。
向build.gradle
文件添加一个依赖build.gradle
:
dependencies {...implementation 'com.okta.spring:okta-spring-boot-starter:1.1.0'...
}
现在更新application.yml
文件:
okta: oauth2: issuer: https://{yourOktaDomain}/oauth2/default client-id: <<yourOktaClientID>>client-secret: <<yourOktaClientSecret>>
spring: thymeleaf: cache: false
填写您的客户密码和客户ID 。
尝试一下。 运行./gradlew bootRun
。
您应该能够通过Okta登录。 这次您将无法选择OAuth 2.0提供程序。
启用用户注册
登录到您的developer.okta.com帐户。
将鼠标悬停在“ 用户”上 ,然后单击“ 注册” 。
启用注册。 此外,启用登录页面中的“显示“注册””链接……选项。 这将导致注册链接显示在托管登录页面中。
使用隐身窗口再次登录。 这次您会注意到该用户可以选择注册为新用户。
输入您的电子邮件地址,密码和名称; 设置一些安全性问题,您就完成了! 您使用Okta为您的应用程序注册了一个新用户。
Okta的自助注册选项可以做更多的事情。 所有这些都可以配置和定制。 看看他们的文档以进行更深入的研究。
在oauth-okta-starter
目录中可以找到使用Okta Spring Boot Starter并删除GitHub OAuth的最终产品。
了解有关Spring Boot登录选项和安全身份验证的更多信息
在本教程中,您涵盖了很多领域。 您使用基本身份验证,基于表单的身份验证和定制的基于表单的身份验证实现了Spring Boot应用程序。 然后,您使用OAuth 2.0和OIDC使用GitHub和Okta实施SSO。 最后,您了解了如何使用Okta Spring Boot Starter简化Spring Boot中的OAuth / OIDC SSO配置,以及如何允许用户通过Okta自助注册。
您可以在GitHub上的本教程中找到示例的所有代码。
要深入研究创建自定义登录表单,请查看有关该主题的Spring文档 。
Spring Security的oauth2login示例还提供了一些很好的信息和更多示例。
以下是一些相关的博客文章,它们演示了如何使用Spring Boot和Spring Security进行登录和身份验证:
- Java应用程序的简单令牌认证
- 在15分钟内使用Spring Boot和Spring Security构建一个Web应用程序
- 创建一个安全的Spring REST API
- 使用Spring Boot和Vue.js构建一个简单的CRUD应用
如果您喜欢这篇文章,请在社交媒体{ Twitter , Facebook , LinkedIn , YouTube }上关注我们,以了解我们何时发布类似的文章。
“ Spring Boot登录选项快速指南”最初于2019年5月15日发布在Okta开发人员博客上。
“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
翻译自: https://www.javacodegeeks.com/2019/06/quick-guide-spring-boot-options.html