Расширенные настройки
Следующие инструкции описывают, как расширить конфигурацию Jmix SAML по умолчанию. Используйте их после выполнения Настройки SAML в Keycloak или аналогичной базовой настройки для другого провайдера.
Сопоставление SAML атрибутов
| Прежде чем сопоставлять атрибуты в приложении, убедитесь, что они включены в SAML assertion. См. добавление атрибутов SAML в Keycloak или аналогичные шаги для вашего провайдера. |
Если SAML assertion содержит информацию о пользователе, такую как имя, должность, отдел или другие данные профиля, вы можете сделать ее доступной в приложении во время пользовательской сессии.
Для этого:
-
Создайте класс, расширяющий
DefaultJmixSamlUserDetails:public class MyUser extends DefaultJmixSamlUserDetails { private String position; (1) public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } }1 Дополнительное поле, соответствующее дополнительному атрибуту SAML. -
Создайте Spring бин, который преобразует входящий SAML assertion в объект пользователя Jmix. Самый простой способ — расширить
BaseSamlUserMapperи переопределить его методы:@Component public class MySamlUserMapper extends BaseSamlUserMapper<MyUser> { @Autowired protected SamlAssertionRolesMapper rolesMapper; @Override protected MyUser initJmixUser(Assertion assertion) { (1) return new MyUser(); } @Override protected void populateUserAttributes(Assertion assertion, OpenSaml4AuthenticationProvider.ResponseToken responseToken, MyUser jmixUser) { (2) Map<String, List<Object>> assertionAttributes = SamlAssertionUtils.getAssertionAttributes(assertion); List<Object> rawValues = assertionAttributes.get("Position"); String positionValue = CollectionUtils.isNotEmpty(rawValues) ? rawValues.get(0).toString() : null; jmixUser.setPosition(positionValue); System.out.println(positionValue); } @Override protected void populateUserAuthorities(Assertion assertion, MyUser jmixUser) { (3) Collection<? extends GrantedAuthority> grantedAuthorities = rolesMapper.toGrantedAuthorities(assertion); jmixUser.setAuthorities(grantedAuthorities); } }1 Этот метод создает объект пользователя Jmix, который будет представлять аутентифицированного пользователя. 2 Здесь значения из SAML Assertionкопируются в ваш объект пользователя. В примере атрибутPositionизAssertionсохраняется в полеpositionобъектаMyUser.3 В этом методе пользователю назначаются authorities. Вместо прямой реализации этой логики, пример делегирует ее интерфейсу SamlAssertionRolesMapperи, следовательно, реализации по умолчаниюDefaultSamlAssertionRolesMapper.
Использование другого атрибута для ролей
По умолчанию DefaultSamlAssertionRolesMapper ищет атрибут с именем Role в SAML Assertion. Ожидается, что этот атрибут содержит коллекцию имен ролей. Для каждого имени роли Jmix ищет ресурсные роли и роли уровня строк с соответствующими кодами. При совпадении кода – соответствующие разрешения добавляются пользователю.
Если ваш провайдер отправляет роли в атрибуте, отличном от Role, вы можете указать необходимый атрибут с помощью следующего свойства Jmix SAML:
jmix.saml.default-saml-assertion-roles-mapper.roles-assertion-attribute=MyRole
Маппер по умолчанию будет читать имена ролей из MyRole вместо Role.
Создание собственного маппера ролей
Если требуется больше контроля, вы можете создать собственный маппер ролей. Это полезно, когда провайдер не отправляет коды ролей Jmix напрямую, а вместо этого отправляет другие значения, которые вы хотите преобразовать в подходящие роли.
Например, значение Manager может приходить из атрибута Position, и ваш маппер может использовать его для назначения подходящих ролей Jmix. Для этого расширьте BaseSamlAssertionRolesMapper и переопределите методы getResourceRolesCodes() и getRowLevelRolesCodes():
@Component
public class MySamlAssertionRolesMapper extends BaseSamlAssertionRolesMapper {
@Override
protected Collection<String> getResourceRolesCodes(Assertion assertion) {
Map<String, List<Object>> assertionAttributes = SamlAssertionUtils.getAssertionAttributes(assertion);
List<Object> rawPositionAttributeValues = assertionAttributes.get("Position");
Collection<String> jmixRoleCodes = new HashSet<>();
rawPositionAttributeValues.stream()
.map(Object::toString)
.forEach(position -> {
if ("Manager".equals(position)) {
jmixRoleCodes.add("edit-contracts");
jmixRoleCodes.add("view-archive");
} else {
jmixRoleCodes.add("view-contracts");
}
});
return jmixRoleCodes;
}
@Override
protected Collection<String> getRowLevelRoleCodes(Assertion assertion) {
// Do something for row-level role codes
return List.of();
}
}
В этом примере значение Position пользователя определяет, какие ресурсные Jmix будут назначены:
-
если
Positionсодержит значениеManager, пользователь получает ролиedit-contractsиview-archive. -
в противном случае пользователь получает роль
view-contracts.
Сохранение пользователей в базе данных
По умолчанию конфигурация Jmix SAML хранит аутентифицированных пользователей только в памяти. Если вы хотите, чтобы пользователи SAML сохранялись в базе данных, выполните следующие шаги:
-
Сделайте сущность User совместимой с дополнение Jmix SAML, расширив абстрактный класс
JmixSamlUserEntity:@JmixEntity @Entity @Table(name = "USER_", indexes = { @Index(name = "IDX_USER__ON_USERNAME", columnList = "USERNAME", unique = true) }) public class User extends JmixSamlUserEntity implements JmixUserDetails, HasTimeZone { //... } -
Зарегистрируйте маппер пользователей на основе
SynchronizingSamlUserMapper; этот суперкласс сохраняет и обновляет пользователя в базе данных, а также может синхронизировать назначения ролей с базой данных:@Component public class MySynchronizingSamlUserMapper extends SynchronizingSamlUserMapper<User> { public MySynchronizingSamlUserMapper() { super(); setSynchronizeRoleAssignments(true); (1) } @Override protected Class<User> getApplicationUserClass() { return User.class; } @Override protected void populateUserAttributes(Assertion assertion, OpenSaml4AuthenticationProvider.ResponseToken responseToken, User jmixUser) { String username = SamlAssertionUtils.getUsername(assertion); Map<String, List<Object>> assertionAttributes = SamlAssertionUtils.getAssertionAttributes(assertion); String firstNameValue = getStringAttributeValue(assertionAttributes, "FirstName", username); String lastNameValue = getStringAttributeValue(assertionAttributes, "LastName", username); jmixUser.setUsername(username); jmixUser.setFirstName(firstNameValue); jmixUser.setLastName(lastNameValue); } protected String getStringAttributeValue(Map<String, List<Object>> assertionAttributes, String attributeName, String username) { List<Object> rawValues = assertionAttributes.get(attributeName); return CollectionUtils.isNotEmpty(rawValues) ? rawValues.get(0).toString() : "%s (%s)".formatted(attributeName, username); } }1 Если установлено значение true, назначения ролей также сохраняются в базе данных.