建筑物身份管理,包括身份验证和授权? 尝试Stormpath! 我们的REST API和强大的Java SDK支持可以消除您的安全风险,并且可以在几分钟内实现。 注册 ,再也不会建立auth了!
每个API开发人员都在寻找一种更安全地管理其应用程序,而又不牺牲速度或易于实现新功能的方法。 为此,我们最近将核心Stormstorm产品(我们的REST API)更新为Spring Boot。 在此过程中,我们利用了许多关键效率,这对于任何使用Spring Boot开发API的人来说都是有价值的。
许多团队发现很难管理对其API的身份验证和访问控制,因此我们希望分享迁移中的一些体系结构原理和技巧,以简化管理Spring Boot API的过程。
注意:下面我们使用命令行工具httpie(https://github.com/jkbrzt/httpie)来练习示例。
1.使用@RestController注释
使用@RestController
(而不是简单地@Controller
)可确保您将返回Java对象,而不是对HTML模板的引用。 像这样:
@RestController
public class HelloController {
@RequestMapping("/")
public String home() {
return "hello";
}
}
执行: http -v localhost:8080
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain;charset=UTF-8
Date: Tue, 14 Jun 2016 23:55:16 GMT
Server: Apache-Coyote/1.1
hello
2.利用自动POJO到JSON转换的优势
Spring Boot会自动为您将POJO(普通的旧Java类)转换为JSON!
@RestController
public class HelloController {
@RequestMapping("/")
public ApiResponse home() {
return new ApiResponse("SUCCESS", "hello");
}
}
public class ApiResponse {
private String status;
private String message;
public ApiResponse(String status, String message) {
this.status = status;
this.message = message;
}
public String getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
执行: http -v localhost:8080
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Tue, 14 Jun 2016 23:54:19 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"message": "hello",
"status": "SUCCESS"
}
3.对自动有线服务使用依赖注入
自动装配服务可以抽象出业务逻辑,而无需进行复杂的Java对象设置,配置或实例化。
@Service
public class HelloService {
public String getGreeting(HttpServletRequest req) {
String greeting = "World";
Account account = AccountResolver.INSTANCE.getAccount(req);
if (account != null) {
greeting = account.getGivenName();
}
return greeting;
}
}
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@RequestMapping("/")
public ApiResponse home(HttpServletRequest req) {
String greeting = helloService.getGreeting(req);
return new ApiResponse("SUCCESS", "Hello " + greeting);
}
}
一旦您通过身份验证,此示例将使用Stormpath返回个性化问候。 为此,您首先需要按照此处概述的方法设置一个Stormpath帐户。 如果您按照说明进行操作,并将Stormpath API密钥文件放在标准位置(~/.stormpath/apiKey.properties)
则无需执行其他任何操作!
启动应用程序并执行以下命令: http -v localhost:8080
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 00:56:46 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"message": "Hello World",
"status": "SUCCESS"
}
接下来,我们需要进行身份验证,以便继续进行示例,因此,我们将使用Stormpath内置的OAuth 2.0功能来进行身份验证并获取个性化消息。 确保已在管理控制台中为Stormpath应用程序创建了一个用户。 有关Java SDK及其集成中Stormpath的OAuth支持的更多信息,请查看我们的Java产品文档
http -v -f POST localhost:8080/oauth/token \
Origin:http://localhost:8080 \
grant_type=password \
username=<email address of the user you setup> \
password=<password of the user you setup>
响应:
HTTP/1.1 200 OK
Cache-Control: no-store
Content-Length: 938
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 00:59:43 GMT
Pragma: no-cache
Server: Apache-Coyote/1.1
{
"access_token": "eyJraWQiOiJSOTJTQkhKQzFVNERBSU1HUTNNSE9HVk1YIiwic3R0IjoiYWNjZXNzIiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiIzVFhQZ01Ld0NiQTk1VEp6VzBXTzRWIiwiaWF0IjoxNDY1OTUyMzgzLCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy82dkZUNEFSZldDbXVIVlY4Vmt0alRvIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zcVlHbUl6VWh4UEtZTzI4a04wSWJSIiwiZXhwIjoxNDY1OTU1OTgzLCJydGkiOiIzVFhQZ0owckkwckFTZUU4SmtmN1NSIn0.o_pIHZVDZWogNuhJN2dmG4UKxACoWFxpRpp5OCyh6C4",
"expires_in": 3600,
"refresh_token": "eyJraWQiOiJSOTJTQkhKQzFVNERBSU1HUTNNSE9HVk1YIiwic3R0IjoicmVmcmVzaCIsImFsZyI6IkhTMjU2In0.eyJqdGkiOiIzVFhQZ0owckkwckFTZUU4SmtmN1NSIiwiaWF0IjoxNDY1OTUyMzgzLCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy82dkZUNEFSZldDbXVIVlY4Vmt0alRvIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zcVlHbUl6VWh4UEtZTzI4a04wSWJSIiwiZXhwIjoxNDcxMTM2MzgzfQ.mJBfCgv4Sdnw7Ubzup7CZ1xdAIC9iO31AJE3NMmp05E",
"token_type": "Bearer"
}
完成后,保存访问令牌以用于我们的应用程序:
ACCESS_TOKEN=eyJraWQiOiJSOTJTQkhKQzFVNERBSU1HUTNNSE9HVk1YIiwic3R0IjoiYWNjZXNzIiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiIzVFhQZ01Ld0NiQTk1VEp6VzBXTzRWIiwiaWF0IjoxNDY1OTUyMzgzLCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy82dkZUNEFSZldDbXVIVlY4Vmt0alRvIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zcVlHbUl6VWh4UEtZTzI4a04wSWJSIiwiZXhwIjoxNDY1OTU1OTgzLCJydGkiOiIzVFhQZ0owckkwckFTZUU4SmtmN1NSIn0.o_pIHZVDZWogNuhJN2dmG4UKxACoWFxpRpp5OCyh6C4
现在,让我们再次通过身份验证访问我们的应用程序:
http -v localhost:8080 Authorization:"Bearer $ACCESS_TOKEN"
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 01:05:35 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"message": "Hello Micah",
"status": "SUCCESS"
}
现在,由于依赖注入,我们从服务中获得了控制器可以访问的个性化响应。
4. Spring Security中的层
Spring Security在Spring应用程序中添加了一个授权层,这使得确定谁应该有权访问什么变得非常容易。 它使用声明性配置语法,并包含注释以限制谁可以访问基于组成员身份和细粒度权限的方法。
如果您想了解更多信息,我还编写了深入的Stormpath + Spring Security教程 。 我们还有一个很棒的教程,可以在开源Java SDK项目中将您从零带到功能完整的Spring Security + Spring Boot WebMVC应用程序 。 在此处找到教程文档。
默认情况下,所有内容都在Spring Security中被锁定,并且Stormpath Spring Security集成是遵循此约定的一个很好的例子。 要尝试使用带有Stormpath的Spring Security,您只需要在如下配置中应用Stormpath集成:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.apply(stormpath()).and()
.authorizeRequests()
.antMatchers("/").permitAll();
}
}
http.apply(stormpath())
是配置Stormpath Spring Security集成所需的全部。 接下来的两行允许未经授权的访问“/”
端点。
让我们看一下这如何影响控制器中的方法:
@RequestMapping("/restricted")
public ApiResponse restricted(HttpServletRequest req) {
// guaranteed to have account because of Spring Security
return new ApiResponse(
"SUCCESS",
"Hello " + AccountResolver.INSTANCE.getAccount(req).getGivenName()
);
}
在这种情况下,无需对帐户执行null检查,因为我们知道,经过身份验证后,进入该方法的唯一方法。 例如:
http localhost:8080/restricted
HTTP/1.1 302 Found
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Wed, 15 Jun 2016 17:32:31 GMT
Expires: 0
Location: http://localhost:8080/login
由于我们未经身份验证,因此我们被重定向到/ login。 如果我像以前一样使用访问令牌,则如下所示:
http localhost:8080/restricted Authorization:"Bearer $ACCESS_TOKEN"
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 17:34:34 GMT
Expires: 0
Pragma: no-cache
{
"message": "Hello Micah",
"status": "SUCCESS"
}
5.统一错误处理
良好的API设计表明,即使出现问题,您的API也会返回通用响应。 这使得将JSON解析和编组为Java对象变得更加容易和可靠。
让我们尝试一个例子。 在这里,我们需要一个标题: Custom-Header
。 如果该标头不存在,则会引发异常:
@RequestMapping("/custom-header")
public ApiResponse customHeader(HttpServletRequest req) throws MissingCustomHeaderException {
String customHeader = req.getHeader("Custom-Header");
if (customHeader == null) {
throw new MissingCustomHeaderException(
"'Custom-Header' on the request is required."
);
}
return new ApiResponse("SUCCESS", "Found Custom-Header: " + customHeader);
}
如果我们看“幸福的道路”,一切都很好:
http localhost:8080/custom-header \
Custom-Header:MyCustomValue \
Authorization:"Bearer $ACCESS_TOKEN"
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 22:28:47 GMT
{
"message": "Found Custom-Header: MyCustomValue",
"status": "SUCCESS"
}
如果没有Custom-Header
标头怎么办?
http localhost:8080/custom-header Authorization:"Bearer $ACCESS_TOKEN"
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 22:34:13 GMT
{
"error": "Internal Server Error",
"exception": "com.stormpath.spring.boot.examples.controller.HelloController$MissingCustomHeaderException",
"message": "'Custom-Header' on the request is required.",
"path": "/custom-header",
"status": 500,
"timestamp": 1466030053360
}
那么,这怎么了? 首先,它不符合我们已经建立的响应格式。 另外,它还会导致500 (Internal Server Error)
错误,这永远是不好的。
幸运的是,Spring Boot使此修复很容易。 我们需要做的就是添加一个异常处理程序。 无需其他代码更改。
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingCustomHeaderException.class)
public ApiResponse exception(MissingCustomHeaderException e) {
return new ApiResponse("ERROR", e.getMessage());
}
现在让我们看一下响应:
http localhost:8080/custom-header Authorization:"Bearer $ACCESS_TOKEN"
HTTP/1.1 400 Bad Request
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Wed, 15 Jun 2016 22:59:32 GMT
{
"message": "'Custom-Header' on the request is required.",
"status": "ERROR"
}
现在我们有了正确的响应400 (Bad Request)
。 我们也有与成功回复相同格式的回复。
额外提示:尝试Stormpath
Stormpath提供了以开发人员为中心的高级身份服务,其中包括身份验证和授权,并且可以在几分钟内实现。 Stormpath REST API使开发人员可以快速轻松地构建他们自己必须编写的各种用户管理功能,包括:
- 完善的授权支持 ,具有缓存功能,可实现最佳性能
- 使用JSON Web令牌和OAuth2进行令牌身份验证和吊销
- 对多租户应用程序的本机支持,以及对客户数据的预先构建的分区
- 全面的文档和对客户服务的承诺-即使是免费的开发人员帐户
- 健壮且惯用的SDK
建筑物身份管理,包括身份验证和授权? 尝试Stormpath! 我们的REST API和强大的Java SDK支持可以消除您的安全风险,并且可以在几分钟内实现。 注册 ,再也不会建立auth了!
翻译自: https://www.javacodegeeks.com/2016/10/5-practical-tips-building-spring-boot-api.html