3.9.11.3. 自定义验证
身份验证机制可以通过密钥、链接、LDAP 登录名和密码等提供访问令牌。REST API 使用特有的身份验证机制,无法被修改。要使用自定义身份验证过程,需要创建 REST 控制器并使用其 URL。
下面我们看看自定义身份验证机制,该机制可以通过推广码获取 OAuth 令牌。在下面的示例中,我们将使用包含带有 code
属性的 Coupon
实体的示例应用程序。我们将此属性的值作为 GET 请求中的身份验证参数发送。
- 创建一个带有
code
属性的Coupon
实体:
@Column(name = "CODE", unique = true, length = 4)
protected String code;
使用 promo-user 登录名创建一个用户,这个用户将会进行验证。
在 web 模块的根包(
com.company.demo
)下创建一个新的名为rest-dispatcher-spring.xml
的 Spring 配置文件。文件内容必须如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.company.demo.web.rest"/>
</beans>
- 将文件包含在
modules/web/src/web-app.properties
文件中的cuba.restSpringContextConfig
应用程序属性中:
cuba.restSpringContextConfig = +com/company/demo/rest-dispatcher-spring.xml
- 在 web 模块的根包下创建
rest
包,并在其中实现自定义 Spring MVC 控制器。在自定义身份验证后,使用OAuthTokenIssuer
bean 为用户生成并发送 REST API 令牌:
@RestController
@RequestMapping("auth-code")
public class AuthCodeController {
@Inject
private OAuthTokenIssuer oAuthTokenIssuer;
@Inject
private LoginService loginService;
@Inject
private Configuration configuration;
@Inject
private DataManager dataManager;
@Inject
private MessageTools messageTools;
// here we check secret code and issue token using OAuthTokenIssuer
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity get(@RequestParam("code") String authCode) {
// obtain system session to be able to call middleware services
WebAuthConfig webAuthConfig = configuration.getConfig(WebAuthConfig.class);
UserSession systemSession;
try {
systemSession = loginService.getSystemSession(webAuthConfig.getTrustedClientPassword());
} catch (LoginException e) {
throw new RuntimeException("Error during system auth");
}
// set security context
AppContext.setSecurityContext(new SecurityContext(systemSession));
try {
// find coupon with code
LoadContext<Coupon> loadContext = LoadContext.create(Coupon.class)
.setQuery(LoadContext.createQuery("select c from demo$Coupon c where c.code = :code")
.setParameter("code", authCode));
if (dataManager.load(loadContext) == null) {
// if coupon is not found - code is incorrect
return new ResponseEntity<>(new ErrorInfo("invalid_grant", "Bad credentials"), HttpStatus.BAD_REQUEST);
}
// generate token for "promo-user"
OAuthTokenIssuer.OAuth2AccessTokenResult tokenResult =
oAuthTokenIssuer.issueToken("promo-user", messageTools.getDefaultLocale(), Collections.emptyMap());
OAuth2AccessToken accessToken = tokenResult.getAccessToken();
// set security HTTP headers to prevent browser caching of security token
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CACHE_CONTROL, "no-store");
headers.set(HttpHeaders.PRAGMA, "no-cache");
return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
} finally {
// clean up security context
AppContext.setSecurityContext(null);
}
}
// POJO for JSON error messages
public static class ErrorInfo implements Serializable {
private String error;
private String error_description;
public ErrorInfo(String error, String error_description) {
this.error = error;
this.error_description = error_description;
}
public String getError() {
return error;
}
public String getError_description() {
return error_description;
}
}
}
- 在 web/core 模块的扫描中排除
rest
包:OAuthTokenIssuer
bean 仅在 REST API 上下文中可用,在应用程序上下文中扫描它会导致错误。
<context:component-scan base-package="com.company.demo">
<context:exclude-filter type="regex" expression="com\.company\.demo\.web\.rest\..*"/>
</context:component-scan>
- 现在,用户将能够使用带有
code
参数的 GET HTTP 请求获取 OAuth2 访问代码
http://localhost:8080/app/rest/auth-code?code=A325
结果将是:
- {"access_token":"74202587-6c2b-4d74-bcf2-0d687ea85dca","token_type":"bearer","expires_in":43199,"scope":"rest-api"}
然后,应将获得的访问令牌传递给 REST API,如文档中所述。