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,如文档中所述。