“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
每个不平凡的应用程序都需要一种保存和更新数据的方法:可通过HTTP访问的资源服务器。 通常,必须保护此数据。 Java是一门伟大的语言,在专业,企业开发方面已有数十年的历史,对于任何应用程序的服务器堆栈都是绝佳的选择。 在Java生态系统内,Spring使为数据构建安全的资源服务器变得简单。 与Okta结合使用时,您可以使用Spring Security将经过专业维护的OAuth和JWT技术轻松集成到Spring Boot中。
在本文中,您将使用Spring Boot和Spring Data JPA构建资源服务器。 最重要的是,您将使用OAuth 2.0实现基于组的身份验证和授权层。 如果这听起来很复杂–不用担心! 不是。
在深入探讨之前,让我们介绍一下背景知识:
资源服务器是服务器功能和数据的编程访问点(与API服务器和/或REST服务器基本相同)。
JPA是Java Persistence API,它是使用Java管理关系数据库的规范。 它描述了Java类和关系数据库之间的抽象层。
Spring Data JPA是JPA提供程序(例如Hibernate)的包装。 正如您将看到的,它使持久化Java类就像添加一些注释和创建简单的存储库接口一样简单。 无需实际编写持久性或检索方法! 另一个很大的好处是您可以透明地更改基础数据库实现,而不必更改任何代码。 例如,在本教程中,您将使用Postgres,但稍后,如果您决定使用MySQL,您要做的就是更改一些依赖项。
安装PostgreSQL以实现JPA持久性
您需要为此教程安装PostgreSQL。 如果尚未安装,请转到其下载页面并进行安装。
接下来需要做的是为项目创建一个Postgres用户和数据库。 为此,您可以使用Postgres CLI。 您应该能够运行以下命令: psql -V
并得到如下响应:
psql (PostgreSQL) 11.12
为您的JPA实体创建PostgreSQL数据库
在使用数据库之前,您需要做一些事情。 你需要:
- 为应用创建用户
- 为该用户设置密码
- 为应用创建数据库
- 为用户授予数据库特权
本教程使用jpatutorial作为用户名,并使用springbootjpa作为数据库名。 如果愿意,可以随意更改这些值,但是您必须记住在整个教程中都使用自定义值。
从终端输入psql
进入Postgres shell。 然后输入以下命令。
创建一个用户
create user jpatutorial;
外壳程序应响应: CREATE ROLE
不要忘记分号! 我永远也不会这样做。 我绝对不是凭经验说话。 但是,如果你没有在分号键入psql
不处理的命令,你可以在沮丧迷茫失去20-30分钟,不知道发生了什么事,直到你进入一个分号,在这一点上,试图处理所有的命令。
给用户密码
alter user jpatutorial with encrypted password '<your really secure password>';
外壳程序应使用以下命令响应: ALTER ROLE
。
创建数据库
create database springbootjpa;
外壳程序应使用以下命令响应: CREATE DATABASE
。
授予特权
grant all privileges on database springbootjpa to jpatutorial;
外壳应以GRANT
响应。
最后,如果需要,键入\q
退出外壳。
如果您想了解更多有关psql
,可以看看Postgres的docs 。
构建一个Spring Boot资源服务器
从GitHub仓库克隆启动项目,并检出start分支:
git clone -b start https://github.com/oktadeveloper/okta-spring-boot-jpa-example.git
入门项目是一个全新的Spring Boot项目,仅具有一些Postgres特定的配置。 如果查看build.gradle
文件,将看到PostgreSQL JPA连接器依赖性。 您还会注意到文件src/main/resources/hibernate.properties
其唯一目的是摆脱对我们而言并不重要的烦人的警告/错误。 src/main/resources/application.yml
文件还为您预先填充了一些属性。
继续并打开application.yml
文件,并填写您为数据库用户创建的密码。 您还应该更新用户名,数据库名称和端口(如果它们不同)。
spring: jpa: hibernate: ddl-auto: create database-platform: org.hibernate.dialect.PostgreSQLDialect datasource: url: "jdbc:postgresql://localhost:5432/springbootjpa" username: jpatutorial password: < your password >
ddl-auto
属性指定了加载时休眠的行为。 选项包括:
- validate: 验证架构,但不做任何更改
- 更新: 更新架构
- create: 创建模式,销毁任何先前的数据
- create-drop: 类似于create,但是在会话关闭时也会删除架构(用于测试)
您正在使用create
。 每次运行该程序时,都会从新表和数据开始创建一个新的数据库。
database-platform
实际上是不必要的。 Spring Data / Hibernate可以自动检测平台。 但是,如果没有此属性,那么如果您在未启动Postgres服务器的情况下运行应用程序,则会得到一个非常无益的错误,即未添加此config属性而不是被告知要启动服务器。 发生这种情况是因为Hibernate无法自动检测数据库平台,因此在抱怨实际上没有正在运行的服务器之前先抱怨一下。
使用./gradlew bootRun
运行应用程序。 您应该会看到以下内容:
2018-11-21 09:27:50.233 INFO 31888 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2018-11-21 09:27:50.302 INFO 31888 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-11-21 09:27:50.308 INFO 31888 --- [ main] c.o.s.SpringBootJpaApplication : Started SpringBootJpaApplication in 21.361 seconds (JVM running for 21.848)
<=========----> 75% EXECUTING [4m 26s]
> :bootRun
但是,它并没有做太多事情。 没有域模型,资源存储库或控制器类。
使用Spring Data和JPA添加域类
域或模型是您将存储的数据的程序表示形式。 Spring Data和JPA的神奇之处在于,Spring可以采用Java类并将其转换为数据库表。 它甚至会自动生成必要的加载和保存方法。 最好的部分是(或多或少)这与数据库无关。
您在本教程中使用的是PostgreSQL,并且可以通过更改build.gradle
文件中的依赖项轻松地将其切换到MySQL。 当然,还要创建一个MySQL数据库并更新application.yml
文件中的必要属性。 这对于测试,开发和长期维护非常有用。
继续阅读以学习如何开发一个简单的服务器来存储皮划艇类型。
在com.okta.springbootjpa
程序包中创建一个名为Kayak.java
的Java文件。 您的皮划艇模型将具有名称,所有者,值和品牌/模型。
package com.okta.springbootjpa;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity // This tells Hibernate to make a table out of this class
@Data // Lombok: adds getters and setters
public class Kayak {public Kayak(String name, String owner, Number value, String makeModel) {this.name = name;this.owner = owner;this.value = value;this.makeModel = makeModel;}@Id@GeneratedValue(strategy=GenerationType.AUTO)private Integer id;private final String name;private String owner;private Number value;private String makeModel;
}
该项目使用Lombok来避免必须编写大量的仪式获取器,设置器和诸如此类的东西。 您可以查看他们的文档 ,或更具体地说是您正在使用的@Data
注释 。
@Entity
注释告诉Spring此类是模型类,应转换为数据库表。
大多数属性可以自动映射。 但是, id
属性用几个注释修饰,因为我们需要告诉JPA这是ID字段,并且应该自动生成它。
使用Spring Data JPA实现CRUD存储库
定义了域类后,Spring知道足以构建数据库表,但是它没有定义任何控制器方法。 没有数据的输出或输入。 Spring使添加资源服务器变得微不足道。 实际上,它是如此琐碎,您可能不会相信。
在包com.okta.springbootjpa
,创建一个名为KayakRepository.java
的接口。
package com.okta.springbootjpa;import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;@RepositoryRestResource
public interface KayakRepository extends CrudRepository<Kayak, Integer> {
}
而已!
现在,您可以从资源服务器创建,读取,更新和删除皮划艇。 在短短的几秒钟内,您将精确地完成此操作,但在此之前,请进行其他更改。
将以下init()
方法添加到SpringBootJpaApplication
类中:
package com.okta.springbootjpa;import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.text.NumberFormat;
import java.text.ParseException;
import java.util.stream.Stream;@SpringBootApplication
public class SpringBootJpaApplication {public static void main(String[] args) {SpringApplication.run(SpringBootJpaApplication.class, args);}@BeanApplicationRunner init(KayakRepository repository) {String[][] data = {{"sea", "Andrew", "300.12", "NDK"},{"creek", "Andrew", "100.75", "Piranha"},{"loaner", "Andrew", "75", "Necky"}};return args -> {Stream.of(data).forEach(array -> {try {Kayak kayak = new Kayak(array[0],array[1],NumberFormat.getInstance().parse(array[2]),array[3]);repository.save(kayak);}catch (ParseException e) {e.printStackTrace();}});repository.findAll().forEach(System.out::println);};}}
应用程序启动时将运行此方法。 它将一些样本数据加载到资源服务器中,只是为了让您在下一节中有所了解。
测试您的Spring Boot资源服务器
HTTPie是一个很棒的命令行实用工具,它使对资源服务器的请求运行变得容易。 如果未安装HTTPie,请使用brew install httpie
进行brew install httpie
。 或前往他们的网站并实现它。 或者只是跟随。
确保您的Spring Boot应用正在运行。 如果不是,请使用./gradlew bootRun
启动它。
针对您的资源服务器运行GET请求: http :8080/kayaks
,这是http GET http://localhost:8080/kayaks
简写。
您会看到以下内容:
HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:39:11 GMT
Transfer-Encoding: chunked{"_embedded": {"kayaks": [{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/1"},"self": {"href": "http://localhost:8080/kayaks/1"}},"makeModel": "NDK","name": "sea","owner": "Andrew","value": 300.12},{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/2"},"self": {"href": "http://localhost:8080/kayaks/2"}},"makeModel": "Piranha","name": "creek","owner": "Andrew","value": 100.75},{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/3"},"self": {"href": "http://localhost:8080/kayaks/3"}},"makeModel": "Necky","name": "loaner","owner": "Andrew","value": 75}]},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}}
}
此输出使您对Spring Boot资源返回的数据格式有了一个非常扎实的想法。 您也可以使用POST添加新的皮划艇。
命令:
http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H"
回复:
HTTP/1.1 201
Content-Type: application/json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:42:14 GMT
Location: http://localhost:8080/kayaks/4
Transfer-Encoding: chunked{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/4"},"self": {"href": "http://localhost:8080/kayaks/4"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500
}
如果您再次列出皮划艇( http :8080/kayaks
),则会在列出的项目中看到新的皮划艇。
HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Date: Wed, 21 Nov 2018 20:44:22 GMT
Transfer-Encoding: chunked{"_embedded": {"kayaks": [...{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/4"},"self": {"href": "http://localhost:8080/kayaks/4"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500}]},...
}
您也可以删除皮划艇。 运行以下命令: http DELETE :8080/kayaks/4
这将删除ID = 4的皮划艇或我们刚刚创建的皮划艇。 第三次获取皮艇列表,您会发现它已经消失了。
使用Spring Boot,只需很少的代码,就可以创建功能全面的资源服务器。 此数据将持久保存到您的Postgres数据库中。
您可以使用Postgres命令外壳来验证这一点。 在终端上,键入psql
进入shell,然后键入以下命令。
连接到数据库:
\connect springbootjpa
psql (9.6.2, server 9.6.6)
You are now connected to database "springbootjpa" as user "cantgetnosleep".
显示表内容:
SELECT * FROM kayak;
id | make_model | name | owner | value
----+------------+--------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------1 | NDK | sea | Andrew | \xaced0005737200106a6176612e6c616e67...8704072c1eb851eb8522 | Piranha | creek | Andrew | \xaced0005737200106a6176612e6c616e672e...0787040593000000000003 | Necky | loaner | Andrew | \xaced00057372000e6a6176612e6c616e67...7870000000000000004b5 | P&H | sea2 | Andrew | \xaced0005737200116a6176612e6...08b0200007870000001f4
(4 rows)
需要注意的几件事。 首先,请注意, 值被存储为二进制对象,因为它被定义为Number
类型而不是原始类型(double,float或int)。 其次,请记住,由于ddl-auto: create
application.yml
文件中的ddl-auto: create
行,在application.yml
每次启动时都将擦除此数据并重新创建整个表。
设置身份验证
Okta是软件即服务身份,身份验证和授权提供者。 虽然我确实从事过将所有项目外包给SaaS提供商的项目,但所产生的问题超出了其承诺解决的问题,但身份验证和授权是使这种模型完全有意义的地方。 在线安全很难。 发现漏洞,必须快速更新服务器。 标准变更和代码需要修改。 所有这些更改都有可能创建新的漏洞。 让Okta处理安全性意味着您可以担心使您的应用程序与众不同的事情。
为了向您展示设置的简便性,您将集成Okta OAuth并将基于令牌的身份验证添加到资源服务器。 如果尚未注册,请访问developer.okta.com并注册一个免费帐户。 拥有帐户后,通过单击“ 应用程序”顶部菜单项,然后单击“ 添加应用程序”按钮,打开开发人员仪表板并创建OpenID Connect(OIDC)应用程序 。
选择单页应用程序 。
默认应用程序设置很好,除了您需要添加登录重定向URI : a
。 您将在稍后使用它来检索测试令牌。
另外,请注意您的客户ID ,稍后您将需要它。
配置您的Spring Boot资源服务器以进行令牌认证
Okta使添加令牌身份验证到Spring Boot非常容易。 他们有一个名为Okta Spring Boot Starter的项目( 请查看GitHub项目 ),将整个过程简化为几个简单的步骤。
在您的build.gradle
文件中添加几个依赖build.gradle
。
compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.0.RELEASE')
compile('com.okta.spring:okta-spring-boot-starter:0.6.1')
将以下内容添加到build.gradle
文件的底部(这解决了logback日志记录依赖性冲突)。
configurations.all { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' exclude group: 'org.springframework.boot', module: 'logback-classic'
}
接下来,你需要一些配置添加到您的application.yml
文件,替换{yourClientId}
从你1563 OIDC应用程序和客户端ID {yourOktaDomain}
与1563网址。 像https://dev-123456.oktapreview.com
东西。
okta: oauth2: issuer: https://{yourOktaDomain}/oauth2/default client-id: {yourClientId} scopes: openid profile email
最后,您需要将@EnableResourceServer
批注添加到SpringBootVueApplication
类。
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;@EnableResourceServer // <- add me
@SpringBootApplication
public class SpringBootJpaApplication { public static void main(String[] args) { SpringApplication.run(SpringBootJpaApplication.class, args); }...
}
测试受保护的Spring Boot服务器
停止您的Spring Boot服务器并使用以下./gradlew bootRun
重新启动它: ./gradlew bootRun
从命令行运行一个简单的GET请求。
http :8080/kayaks
您会得到未经授权的401 /。
HTTP/1.1 401
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8{"error": "unauthorized","error_description": "Full authentication is required to access this resource"
}
生成访问令牌
要立即访问服务器,您需要一个有效的访问令牌。 您可以使用OpenID Connect调试器来帮助您完成此任务。 在另一个窗口中,打开oidcdebugger.com 。
授权URI : https://{yourOktaUrl}/oauth2/default/v1/authorize
,其中{yourOktaUrl}
替换为您的实际Okta预览URL。
重定向URI :不变。 这是您在上面的OIDC应用程序中添加的值。
客户ID :来自您刚创建的OIDC应用程序。
范围 : openid profile email
。
状态 :您要通过OAuth重定向过程传递的任何值。 我将其设置为{}
。
Nonce :可以一个人呆着。 Nonce表示“编号已使用一次”,是一种简单的安全措施,用于防止同一请求被多次使用。
响应类型 : token
。
响应方式 : form_post
。
点击发送请求 。 如果您尚未登录developer.okta.com,则需要登录。如果(可能的话)已经登录,则将为您的登录身份生成令牌。
使用访问令牌
您可以通过在Bearer类型的Authorization请求标头中包含令牌来使用令牌。
Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJfNVJr...
通过HTTPie发出请求:
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1...'
添加基于组的授权
到目前为止,授权方案还算是二进制的。 该请求是否带有有效令牌。 现在,您将添加基于组的身份验证。 请注意,尽管有时在臭名昭著的网站上可以互换使用,但角色和组却不是一回事,它们是实现授权的不同方法。
角色是用户可以继承的权限集合的集合。 组是一组标准权限分配给的用户的集合。 但是,在令牌的范围以及如何将Spring Security与JPA结合使用时,实现是完全相同的。 它们都以字符串“ authority”的形式从OAuth OIDC应用程序传递给Spring,因此目前它们基本上可以互换。 不同之处在于受保护的内容及其定义方式。
要在Okta中使用基于组的授权,您需要在访问令牌中添加一个“组”声明。 创建一个Admin
组(“ 用户” >“ 组” >“ 添加组” )并将您的用户添加到其中。 您可以使用注册时使用的帐户,也可以创建一个新用户(“ 用户” >“ 添加人” )。 导航到“ API” >“ 授权服务器” ,单击“ 授权服务器”选项卡,然后编辑默认选项卡。 点击索赔标签,然后添加索赔 。 将其命名为“组”,并将其包含在访问令牌中。 将值类型设置为“ Groups”,并将过滤器设置为.*
的正则表达式。
使用OIDC调试器创建一个新的访问令牌。 通过转到jsonwebtoken.io并输入生成的访问令牌,来查看已解码的令牌。
有效载荷看起来像这样:
{"ver": 1,"jti": "AT.Hk8lHezJNw4wxey1czypDiNXJUxIlKmdT16MrnLGp9E","iss": "https://dev-533919.oktapreview.com/oauth2/default","aud": "api://default","iat": 1542862245,"exp": 1542866683,"cid": "0oahpnkb44pcaOIBG0h7","uid": "00ue9mlzk7eW24e8Y0h7","scp": ["email","profile","openid"],"sub": "andrew.hughes@mail.com","groups": ["Everyone","Admin"]
}
组声明包含用户分配到的组。 您用来登录developer.okta.com网站的用户也将是“所有人”组和“管理员”组的成员。
为了使Spring Boot和资源服务器在基于组的授权下都能正常运行,您需要对代码进行一些更改。
首先,在com.okta.springbootjpa
包中添加一个名为SecurityConfiguration
的新Java类。
package com.okta.springbootjpa;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
}
需要此配置类来启用@PreAuthorize
批注,该批注将用于基于组成员身份保护资源服务器。
接下来,将@PreAuthorize
批注添加到KayakRepository
,如下所示:
...
import org.springframework.security.access.prepost.PreAuthorize;
...@RepositoryRestResource
@PreAuthorize("hasAuthority('Admin')")
public interface KayakRepository extends CrudRepository<Kayak, Long> {
}
最后,在SpringBootJpaApplication
, 删除 ApplicationRunner init(KayakRepository repository)
方法(或仅注释掉@Bean
批注)。 如果跳过此步骤,构建将失败,并显示以下错误:
AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
@PreAuthorize
批注实际上阻止init()
方法以编程方式创建自举数据,因为没有用户登录。因此,该方法运行时会引发错误。
请注意,您在@PreAuthorize
批注中使用hasAuthority()
,而不是 hasRole()
。 区别在于hasRole()
期望组或角色在ALL CAPS中并且具有ROLE_
前缀。 这可以被配置,当然,但hasAuthority()
来没有这个包袱,简单地检查任何权利要求你定义为okta.oauth2.roles-claim
在application.yml
。
在您的Spring Boot应用程序中测试管理员用户
重新启动您的Spring Boot应用程序(以./gradlew bootRun
)。
尝试未经身份验证的GET请求:v class =“ highlighter-rouge”> http:8080 / kayaks。
HTTP/1.1 401
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8{"error": "unauthorized","error_description": "Full authentication is required to access this resource"
}
使用令牌尝试一下。
命令:
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf...'
回复:
HTTP/1.1 200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/hal+json;charset=UTF-8{"_embedded": {"kayaks": []},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}}
}
有效! 我们没有任何皮划艇,因为我们必须删除上面的init()
方法,因此_embedded.kayaks
数组为空。
提示:今后,如果您不想复制并粘贴整个巨大的令牌字符串,则可以将其存储到shell变量中,然后像这样重用它:
TOKEN=eyJraWQiOiJldjFpay1DS3UzYjJXS3QzSVl1MlJZc3VJSzBBYUl3NkU4SDJf...
http :8080/kayaks 'Authorization: Bearer $TOKEN'
创建一个非管理员用户
为了演示基于组的授权,您需要在Okta上创建一个不是管理员的新用户。 转到developer.okta.com仪表板。
从顶部菜单中,选择“ 用户和人员” 。
单击添加人按钮。
给用户一个名字 , 姓氏和用户名 (也将是主要电子邮件 )。 值无关紧要,并且您将不必检查电子邮件。 您只需要知道电子邮件地址/用户名和密码,即可在一分钟内登录Okta。
密码 :将下拉菜单更改为“ 由管理员设置” 。
为用户分配密码。
点击保存 。
您刚刚创建的用户不是Admin组的成员,而是默认组Everyone的成员。
在Spring Boot应用程序中基于测试组的授权
注销您的Okta开发人员仪表板。
返回OIDC调试器并生成一个新令牌。
这次,以新的非管理员用户身份登录。 系统会要求您选择一个安全问题,然后将您重定向到https://oidcdebugger.com/debug
页面,您可以在其中复制令牌。
如果愿意,可以转到jsonwebtoken.io并解码新令牌。 在有效内容中, 子声明将显示用户的电子邮件/用户名,而组声明将仅显示“ 所有人”组。
{..."sub": "test@gmail.com","groups": ["Everyone"]
}
如果使用新令牌在/kayaks
端点上发出请求,则会收到403 / Access Denied。
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'
HTTP/1.1 403
...{"error": "access_denied","error_description": "Access is denied"
}
为了演示@PreAuthorize
批注的真正功能,请创建一个方法级别的安全约束。 将KayakRepository
类更改为以下内容:
@RepositoryRestResource
public interface KayakRepository extends CrudRepository<Kayak, Long> { @PreAuthorize("hasAuthority('Admin')") <S extends Kayak> S save(S entity); }
这仅将save()
方法限制为Admin组的成员。 仅需身份验证即可限制存储库的其余部分,而无需特定的组成员身份。
重新启动Spring Boot服务器。 再次运行相同的请求。
http :8080/kayaks 'Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX...'
HTTP/1.1 200
...{"_embedded": {"kayaks": []},"_links": {"profile": {"href": "http://localhost:8080/profile/kayaks"},"self": {"href": "http://localhost:8080/kayaks"}}
}
_.embedded.kayaks
存储库为空,因此_.embedded.kayaks
是一个空数组。
尝试创建一个新的皮划艇。
http POST :8080/kayaks name="sea2" owner="Andrew" value="500" makeModel="P&H" "Authorization: Bearer eyJraWQiOiJldjFpay1DS3UzYjJX..."
您将获得另一个403。“保存”在此处等于HTML POST。
但是,如果您使用从原始管理员帐户生成的令牌,则可以使用。
注意:您的令牌可能已过期,您必须再次注销developer.okta.com,然后在OIDC调试器上重新生成令牌。
使用您的管理员帐户生成的令牌发布新的皮划艇。
这次您将获得201。
HTTP/1.1 201
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
...{"_links": {"kayak": {"href": "http://localhost:8080/kayaks/1"},"self": {"href": "http://localhost:8080/kayaks/1"}},"makeModel": "P&H","name": "sea2","owner": "Andrew","value": 500
}
成功!
看一下Spring Data的CrudRepository
接口,以了解可以被覆盖的方法以及分配给方法级安全性的方法。 @PreAuthorize
注释不仅可以用于组,还可以使用更多的内容。 可以利用Spring的表达语言(SpEL)的全部功能。
public interface CrudRepository<T, ID> extends Repository<T, ID> {<S extends T> S save(S entity);<S extends T> Iterable<S> saveAll(Iterable<S> entities);Optional<T> findById(ID id);boolean existsById(ID id);Iterable<T> findAll();Iterable<T> findAllById(Iterable<ID> ids);long count();void deleteById(ID id);void delete(T entity);void deleteAll(Iterable<? extends T> entities);void deleteAll();
}
就是这样! 很酷吧? 在本教程中,您将建立一个PostgreSQL数据库,创建一个Spring Boot资源服务器,该服务器使用Spring Data和JPA来持久化数据模型,然后将该数据模型转换为REST API,只需很少的代码。 此外,您还使用Okta向服务器应用程序添加了OIDC身份验证和OAuth 2.0授权。 最后,您实现了一个简单的基于组的授权方案。
如果您想查看这个完整的项目,可以在GitHub上的仓库中找到@ oktadeveloper / okta-spring-boot-jpa-example 。
请留意本系列中的下一篇文章,该文章将介绍在Spring WebFlux中使用NoSQL数据库(MongoDB)。
了解有关Spring Boot,Spring Security和安全身份验证的更多信息
如果您想了解有关Spring Boot,Spring Security或现代应用程序安全性的更多信息,请查看以下任何出色的教程:
- Spring Boot,OAuth 2.0和Okta入门
- 15分钟内将单一登录添加到您的Spring Boot Web App
- 使用多重身份验证保护您的Spring Boot应用程序安全
- 使用Spring Boot和GraphQL构建安全的API
如果您想深入研究,请查看Okta Spring Boot Starter GitHub项目 。
这对于Spring Data和保护Spring Boot项目是一个很好的参考: https : //docs.spring.io/spring-data/rest/docs/current/reference/html/
当将PostgreSQL与JPA和Hibernate一起使用时, Vlad Mihalcea有一个很棒的教程,标题为《 9个高性能技巧》 。
Baeldung有一个关于保护Spring Data / Spring Boot项目中的方法的有用教程: https ://www.baeldung.com/spring-security-method-security
最后,如果您需要在Mac OS X上使用PostgreSQL的更多帮助,请参阅此codementor.io教程 。
如果您对此帖子有任何疑问,请在下面添加评论。 有关更多精彩内容, 请在Twitter上关注@oktadev , 在Facebook上关注我们,或订阅我们的YouTube频道 。
``使用PostgreSQL使用Spring Boot和JPA构建应用程序''最初于2018年12月13日发布在Okta开发者博客上。
“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
翻译自: https://www.javacodegeeks.com/2018/12/build-basic-spring-boot-using-postgresql.html