3.9.11.3. 自定义验证

身份验证机制可以通过密钥、链接、LDAP 登录名和密码等提供访问令牌。REST API 使用特有的身份验证机制,无法被修改。要使用自定义身份验证过程,需要创建 REST 控制器并使用其 URL。

下面我们看看自定义身份验证机制,该机制可以通过推广码获取 OAuth 令牌。在下面的示例中,我们将使用包含带有 code 属性的 Coupon 实体的示例应用程序。我们将此属性的值作为 GET 请求中的身份验证参数发送。

  • 创建一个带有 code 属性的 Coupon 实体:
  1. @Column(name = "CODE", unique = true, length = 4)
  2. protected String code;
  • 使用 promo-user 登录名创建一个用户,这个用户将会进行验证。

  • web 模块的根包(com.company.demo)下创建一个新的名为 rest-dispatcher-spring.xml 的 Spring 配置文件。文件内容必须如下:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. 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">
  5. <context:component-scan base-package="com.company.demo.web.rest"/>
  6. </beans>
  • 将文件包含在 modules/web/src/web-app.properties 文件中的 cuba.restSpringContextConfig 应用程序属性中:
  1. cuba.restSpringContextConfig = +com/company/demo/rest-dispatcher-spring.xml
  • web 模块的根包下创建 rest 包,并在其中实现自定义 Spring MVC 控制器。在自定义身份验证后,使用 OAuthTokenIssuer bean 为用户生成并发送 REST API 令牌:
  1. @RestController
  2. @RequestMapping("auth-code")
  3. public class AuthCodeController {
  4. @Inject
  5. private OAuthTokenIssuer oAuthTokenIssuer;
  6. @Inject
  7. private LoginService loginService;
  8. @Inject
  9. private Configuration configuration;
  10. @Inject
  11. private DataManager dataManager;
  12. @Inject
  13. private MessageTools messageTools;
  14. // here we check secret code and issue token using OAuthTokenIssuer
  15. @RequestMapping(method = RequestMethod.GET)
  16. public ResponseEntity get(@RequestParam("code") String authCode) {
  17. // obtain system session to be able to call middleware services
  18. WebAuthConfig webAuthConfig = configuration.getConfig(WebAuthConfig.class);
  19. UserSession systemSession;
  20. try {
  21. systemSession = loginService.getSystemSession(webAuthConfig.getTrustedClientPassword());
  22. } catch (LoginException e) {
  23. throw new RuntimeException("Error during system auth");
  24. }
  25. // set security context
  26. AppContext.setSecurityContext(new SecurityContext(systemSession));
  27. try {
  28. // find coupon with code
  29. LoadContext<Coupon> loadContext = LoadContext.create(Coupon.class)
  30. .setQuery(LoadContext.createQuery("select c from demo$Coupon c where c.code = :code")
  31. .setParameter("code", authCode));
  32. if (dataManager.load(loadContext) == null) {
  33. // if coupon is not found - code is incorrect
  34. return new ResponseEntity<>(new ErrorInfo("invalid_grant", "Bad credentials"), HttpStatus.BAD_REQUEST);
  35. }
  36. // generate token for "promo-user"
  37. OAuthTokenIssuer.OAuth2AccessTokenResult tokenResult =
  38. oAuthTokenIssuer.issueToken("promo-user", messageTools.getDefaultLocale(), Collections.emptyMap());
  39. OAuth2AccessToken accessToken = tokenResult.getAccessToken();
  40. // set security HTTP headers to prevent browser caching of security token
  41. HttpHeaders headers = new HttpHeaders();
  42. headers.set(HttpHeaders.CACHE_CONTROL, "no-store");
  43. headers.set(HttpHeaders.PRAGMA, "no-cache");
  44. return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
  45. } finally {
  46. // clean up security context
  47. AppContext.setSecurityContext(null);
  48. }
  49. }
  50. // POJO for JSON error messages
  51. public static class ErrorInfo implements Serializable {
  52. private String error;
  53. private String error_description;
  54. public ErrorInfo(String error, String error_description) {
  55. this.error = error;
  56. this.error_description = error_description;
  57. }
  58. public String getError() {
  59. return error;
  60. }
  61. public String getError_description() {
  62. return error_description;
  63. }
  64. }
  65. }
  • web/core 模块的扫描中排除 rest 包:OAuthTokenIssuer bean 仅在 REST API 上下文中可用,在应用程序上下文中扫描它会导致错误。
  1. <context:component-scan base-package="com.company.demo">
  2. <context:exclude-filter type="regex" expression="com\.company\.demo\.web\.rest\..*"/>
  3. </context:component-scan>
  • 现在,用户将能够使用带有 code 参数的 GET HTTP 请求获取 OAuth2 访问代码

http://localhost:8080/app/rest/auth-code?code=A325

结果将是:

  1. {"access_token":"74202587-6c2b-4d74-bcf2-0d687ea85dca","token_type":"bearer","expires_in":43199,"scope":"rest-api"}

然后,应将获得的访问令牌传递给 REST API,如文档中所述。