4.4.3. Собственный механизм аутентификации
Различные механизмы аутентификации могут предоставлять токен по ключу, по ссылке, по логину и паролю LDAP и т.д. Стандартный механизм аутентификации в REST API изменить нельзя, но можно создать свой механизм. Для этого необходимо создать REST-контроллер, который предоставит свой URL для входа в приложение.
В этом примере мы рассмотрим механизм аутентификации, позволяющий получить OAuth-токен по промо-коду. За основу возьмём приложение, содержащее сущность Coupon
(Купон) с атрибутом code
(промо-код). Значение этого атрибута мы будем передавать в качестве параметра аутентификации в GET-запросе.
-
Создайте сущность
Coupon
и добавьте ей атрибутcode
:@Column(name = "CODE", unique = true, length = 4) protected String code;
-
Создайте нового пользователя с логином promo-user, от лица которого будет выполняться аутентификация по промо-коду.
-
В корневом каталоге модуля web (
com.company.demo
) создайте новый файл конфигурации Springrest-dispatcher-spring.xml
со следующим содержанием:<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>
-
Ссылку на этот файл укажите в свойстве приложения
cuba.restSpringContextConfig
в файлеmodules/web/src/web-app.properties
:cuba.restSpringContextConfig = +com/company/demo/rest-dispatcher-spring.xml
-
Создайте пакет
rest
в корневом каталоге модуля web, а в нём - свой контроллер Spring MVC. В контроллере используйте бинOAuthTokenIssuer
, который позволяет сгенерировать и выдать 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; } } }
-
Исключите пакет
rest
из сканирования в модулях web/core: это необходимо, так как бинOAuthTokenIssuer
доступен только внутри контекста REST API, и сканирование его в контексте приложения будет вызывать ошибку.<context:component-scan base-package="com.company.demo"> <context:exclude-filter type="regex" expression="com\.company\.demo\.web\.rest\..*"/> </context:component-scan>
-
Теперь пользователи могут получать код доступа OAuth2 через обычный запрос GET HTTP, передавая значение промо-кода в параметре
code
: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"}
Теперь полученный access token нужно передавать в REST API, как описано в общей документации.