Я занимаюсь большим количеством исследований по этому вопросу, и есть множество примеров, но я поражен выборами, но я собрал код, чтобы сделать то, что я хотеть.Spring Security 4 с токеном аутентификации сторонней организации
У меня есть Apache Load Balancer, который перенаправляет меня на страницу входа для нашего OpenAM 10. Когда я иду в https://portal.mydomain.com/myapp я попадаю на: https://sso.mydomain.net:9443/sso/UI/Login?module=AGMAuth&goto=https%3A%2F%2Fvmlb.mydomain.net%3A443%2Fmyapp
Это моя страница Логин, я попросил имя пользователя и пароль, и я возвращаю токен, который выглядит как (AQIC5wM2LY4Sfcyl9raxhA-xBE14_4QBbkjXi-rFn43PMpc. AAJTSQACMDE.) в файл cookie. Из внешнего интерфейса я могу получить маркер из файла cookie, я могу добавить его в заголовок запроса для каждого вызова моего внутреннего контура, который является back-end SpringMVC RESTful.
Просто FYI, я знаю, как вызвать OpenAM RESTful api, передавая этот токен и возвращая имя пользователя и другую информацию. Роли не хранятся в OpenAM. Если в back-end моих веб-сервисов RESTful я вызываю OpenAM с токеном ... если я не верну какие-либо данные, то я хочу вернуться к своей странице авторизации аутентификации: https://sso.mydomain.net:9443/sso/UI/Login?module=AGMAuth&goto=https%3A%2F%2Fvmlb.mydomain.net%3A443%2Fmyapp
Здесь является контроллером одного из моих обеспеченного URL,:
@RequestMapping(value = "/trialId/{trialId}", method = RequestMethod.GET, headers = "Accept=application/json")
public @ResponseBody TrialEntity getTrialById(@PathVariable("trialId") long trialId)
{
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println("TrialController: getTrialById: principal");
UserAccountEntity user = null;
if (principal instanceof UserAccountEntity)
{
user = ((UserAccountEntity) principal);
}
TrialEntity trialEntity = service.getById(trialId);
System.out.println("TrialController: retrieveTrial: trialEntity=" + trialEntity);
return trialEntity;
}
мне нужно иметь ли каждый безопасный URL попытаться получить принципал?
Теперь вот мой файл безопасности spring-security.XML. Это не может быть правильным, но это, где я начал:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<http realm="Protected API"
use-expressions="true"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="restAuthenticationManager">
<custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<intercept-url pattern="/corelabs" access="permitAll" />
<intercept-url pattern="/sponsors" access="permitAll" />
<intercept-url pattern="/trials/trialId/**" access="hasRole('trialAdmin')" />
<intercept-url pattern="/subjects" access="hasRole('siteSender') and hasRole('trialManager')" />
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="customUserDetailsService" class="com.agmednet.server.security.CustomUserDetailsService" />
</beans:beans>
Теперь вот некоторый код безопасности на заднем конце. Это код, который возвращается к базе данных, чтобы получить данные пользователя на основе имени пользователя, и я также могу загрузить роли для этого пользователя. Я получил это из одного из примеров Spring Security 4.
@Service("customUserDetailsService")
@Transactional
public class CustomUserDetailsService implements UserDetailsService
{
private static final Log logger = LogFactory.getLog(CustomUserDetailsService.class);
@Autowired
private UserAccountDao userAccountDao;
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
System.out.println("CustomUserDetailsService: username : " + username);
UserAccountEntity userAccountEntity = userAccountDao.getByUsername(username);
System.out.println("CustomUserDetailsService: userAccountEntity : " + userAccountEntity);
if (userAccountEntity == null)
{
System.out.println("CustomUserDetailsService: userAccountEntity not found");
throw new UsernameNotFoundException("Username not found");
}
System.out.println("CustomUserDetailsService: START: springframework.user");
// this "User" object is: org.springframework.security.core.userdetails.User
User user = new User(userAccountEntity.getUsername(), userAccountEntity.getPassword(), true, true, true, true,
getGrantedAuthorities(userAccountEntity));
System.out.println("CustomUserDetailsService: FINISH: springframework.user = " + user);
return user;
}
private List<GrantedAuthority> getGrantedAuthorities(UserAccountEntity userAccountEntity)
{
System.out.println("getGrantedAuthorities: START");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (UserRoleEntity userRoleEntity : userAccountEntity.getUserRoleList())
{
System.out.println("getGrantedAuthorities: UserRoleEntity : " + userRoleEntity);
authorities.add(new SimpleGrantedAuthority("ROLE_" + userRoleEntity.getUserRoleName()));
}
System.out.print("authorities :" + authorities);
return authorities;
}
}
Вот еще один код безопасности, который я не уверен, работает ли это для меня или нет. Поскольку в этом случае я действительно смотрю стратегии предварительной аутентификации, и я не уверен, действительно ли кто-то из них применим в этом случае. Я также вижу, что я могу реализовать RequestHeaderFilter, но я также не знаю, как это вписывается в.
public class CustomAuthenticationProvider implements AuthenticationProvider
{
@Autowired
private CustomUserDetailsService userService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails user = userService.loadUserByUsername(username);
if (user == null || !user.getUsername().equalsIgnoreCase(username))
{
throw new BadCredentialsException("Username not found.");
}
if (!password.equals(user.getPassword()))
{
throw new BadCredentialsException("Wrong password.");
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
public boolean supports(Class<?> arg0)
{
return true;
}
}
Итак, я потянув в какой-то код и пытаясь понять, как это все работает на основе многих небольшие вариации в отношении того, как другие люди имеют рабочие решения. Опять же, у меня есть токен, который исходит от стороннего API и помещается в файл cookie. Итак, я смотрел на сценарии с помпой, которые абсолютно для меня имеют смысл ... кроме того, когда токен недействителен, истекает или не существует, я перенаправляюсь на эту страницу третьей стороны. Мне показалось, что я видел пример пользовательской формы входа, которому был задан URL-адрес, поэтому, если аутентификация недействительна (токен либо не существует, либо недействителен), мы не авторизованы и отправлены обратно на этот пользовательский URL-адрес, который будет работать для меня.
Основываясь на том, что я уже добавил, я должен иметь возможность получить имя пользователя из токена после того, как передам его в OpenAM ... но мне нужен фильтр-запрос-запрос какого-то типа, чтобы получить его сначала ... тогда использовать это имя пользователя для загрузки пользовательских деталей и ролей и поместить их в SecurityContext?
Я просто noobie для всех этих трубопроводов Spring Security, поэтому любая помощь с этой конкретной установкой была бы очень полезной. И BTW, я не возражаю против использования XML для конфигурации весной безопасности. Если мне нужно добавить свой веб-сайт.xml, я тоже могу это сделать, но, пожалуйста, предположим, что я правильно установил свой web.xml. Благодаря!
ОБНОВЛЕНО: У меня есть тестовое устройство, которое выглядит как:
@Test
public void testMockGetById() throws Exception
{
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(BASE_URL + "/trialId/149");
this.mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isOk());
}
На примере SiteMinder, который предлагается ниже. Я теперь ищу заголовок запроса под названием «openam_token», а не «SM_USER». Я уже тестировал это без токена, и получаю ошибку 500, что нет токена.
Итак, теперь мне нужно изменить мой модульный тест, чтобы добавить токен в заголовок запроса. Этот токен вернет правильного пользователя, который имеет доступ к этому URL-адресу. Второй тест состоит в том, чтобы иметь токен, который исходит от другого пользователя, который не имеет доступа к этому URL-адресу. Если мои условия тестирования будут работать, я считаю это победой.
Хорошо ... так, я пытаюсь реализовать этот пример, и кажется, что единственный код, который мне нужно будет добавить, это CustomUserDetailsService, который у меня уже был, так что это хорошо. но этот siteMinderFilter, он ищет заголовок запроса SM_USER, я полагаю, я мог бы изменить его на любой другой заголовок? И это не имя пользователя, это токен ... поэтому я задаюсь вопросом, могу ли я изменить loadUserName, чтобы вместо этого взять токен-строку, а затем сделать вызов OpenAM, чтобы получить имя пользователя ... Как вы думаете, это сработает? – tjholmes66
Да, имя заголовка может быть любым, что вы хотите (primaryRequestHeader). Служба пользовательских данных представляет собой пользовательскую реализацию и может получать данные на основе этого токена. – 6ton
Я только что обновил вопрос для модульного теста. Мне нужно добавить заголовок в конструктор запросов, который не должен быть слишком сложным для выполнения, но при попытке получить сообщение об ошибке, которое имеет значение SecurityContext в моем тесте. Какие-либо советы по правильному тестированию устройства, чтобы убедиться, что это правильно? Благодаря! – tjholmes66