UserServiceImpl:
@Override public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException{ String userID = credential.getNameID().getValue(); logger.info(userID + " is logged in"); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER"); authorities.add(authority); List<Attribute> userAttributes = credential.getAttributes(); logger.info("Credential attributes: " + userAttributes); for (int attrIndex = 0; attrIndex < userAttributes.size(); attrIndex++) { Attribute attr = userAttributes.get(attrIndex); List<XMLObject> attrValues = attr.getAttributeValues(); StringBuilder strBuilder = new StringBuilder(); for (int attrValueIndex = 0; attrValueIndex < attrValues.size(); attrValueIndex++) { XMLObject currObj = attrValues.get(attrValueIndex); strBuilder.append(currObj.toString()).append(","); } strBuilder.deleteCharAt(strBuilder.length() - 1); logger.info(attr.getFriendlyName() + ", " + strBuilder.toString()); String samlAttrValue = strBuilder.toString(); switch (attr.getFriendlyName()) { case "userName": samlUserAttribute.setUserName(samlAttrValue); case "email": samlUserAttribute.setEmail(samlAttrValue); break; case "firstName": samlUserAttribute.setFirstName(samlAttrValue); break; case "lastName": samlUserAttribute.setLastName(samlAttrValue); break; case "userType": samlUserAttribute.setUserType(samlAttrValue); break; case "accountName": samlUserAttribute.setAccountName(samlAttrValue); break; case "contactId": samlUserAttribute.setContactId(samlAttrValue); break; default: logger.info("invalid attribute name" + attr.getFriendlyName()); } } logger.info("User details obtained: " + samlUserAttribute); return new SamlUserDTO(userID, "<abc123>", authorities, samlUserAttribute); }
Реализация пользовательских сведений:
private SamlUserAttribute currentUserAttribute; private String password; private final String username; private final Set<GrantedAuthority> authorities; private final boolean accountNonExpired; private final boolean accountNonLocked; private final boolean credentialsNonExpired; private final boolean enabled; .. setter/getter/contructors for userDetails...
3. Детали конфигурации безопасности
<security:http entry-point-ref="samlEntryPoint" access-decision-manager-ref="accessDecisionManager" authentication-manager-ref="authenticationManager" use-expressions="true"> <security:intercept-url pattern="/login" access="permitAll"/> <security:intercept-url pattern="/**" access="isFullyAuthenticated()" /> <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" /> <security:csrf disabled="true"/> </security:http> <bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy"> <security:filter-chain-map request-matcher="ant"> <security:filter-chain pattern="/**" filters="samlEntryPoint" /> </security:filter-chain-map> </bean> <bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider"> <property name="userDetails" ref="UserServiceImpl" /> <property name="forcePrincipalAsString" value="false" /> </bean> <bean id="UserServiceImpl" class="com.akamai.marketplace.service.impl.common.UserServiceImpl"> </bean>
При переходе по требуемому URL-адресу я вижу в браузере, что происходит перенаправление IDP, IDP отвечает требуемыми утверждениями, а целевой URL-адрес — /login. Но в моем контроллере /login UserDetail объекта аутентификации не был заполнен пользовательскими данными.
/логин контроллера:
@RequestMapping(path="/login",method = RequestMethod.POST) public ResponseEntity<SamlUserDTO> login() { logger.info("login API reached through IdP."); Authentication userAuthentication = SecurityContextHolder.getContext().getAuthentication(); logger.info("user details: "+userAuthentication.getDetails()); logger.info("user credentials: "+userAuthentication.getCredentials()); logger.info("principal " + userAuthentication.getPrincipal()); SamlUserDTO samlUserDTO1 = (SamlUserDTO) userAuthentication.getPrincipal(); return ResponseEntity.ok(samlUserDTO1); }
журналы:
2016-08-11 17:22:16 DEBUG BaseMessageEncoder: 56 — успешно закодированное сообщение. 2016-08-11 17:22:16 DEBUG HttpSessionStorage:93 — Сохранение сообщения a399ehchh04afi304hih7e49fd791g2 в сеансе someValue
2016-08-11 17:22:16 ИНФОРМАЦИЯ SAMLDefaultLogger:127 - AuthNRequest;УСПЕХ;someIP;SP-entityId;Idp-entityId;;;
11.08.2016, 17:22:37 INFO UserController:18 — API входа через IdP.
2016-08-11 17:22:37 INFO UserController:20 — сведения о пользователе: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffa64e: RemoteIpAddress: Idp-IpAddress; Идентификатор сеанса: какое-то значение
2016-08-11 17:22:37 INFO UserController:21 - учетные данные пользователя:
2016-08-11 17:22:37 ИНФОРМАЦИЯ UserController:22 — основной анонимный пользователь 2016-08-11 17:22:37 DEBUG ExceptionHandlerExceptionResolver:133 — Разрешение исключения из обработчика .controller.UserController.login()]: java.lang.ClassCastException: java.lang.String нельзя преобразовать в controller.model.SamlUserDTO
2016-08-11 17:22:37 DEBUG ExceptionHandlerExceptionResolver:361 - Вызов метода @ExceptionHandler: public org.springframework.http.ResponseEntity RESTExceptionHandler.handleExeption(java.lang.Exception) 2016-08-11 17:22:37 ОШИБКА RESTExceptionHandler :60 — Исключение, перехваченное обработчиком исключений: java.lang.ClassCastException: java.lang.String нельзя преобразовать в package.SamlUserDTO