2015-11-18 1 views
0

Я занимаюсь большим количеством исследований по этому вопросу, и есть множество примеров, но я поражен выборами, но я собрал код, чтобы сделать то, что я хотеть.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-адресу. Если мои условия тестирования будут работать, я считаю это победой.

ответ

1

Аутентификация Siteminder аналогичным образом настраивает заголовок запроса на балансировщике нагрузки (Apache). Я бы рекомендовал посмотреть примеры того, как siteminder настроен на весеннюю безопасность. См. this и весенний документ here.

Не думайте, что вам нужен CustomAuthenticationProvider, так как проверка подлинности обрабатывается до того, как запросы достигнут вашего API для отдыха.

+0

Хорошо ... так, я пытаюсь реализовать этот пример, и кажется, что единственный код, который мне нужно будет добавить, это CustomUserDetailsService, который у меня уже был, так что это хорошо. но этот siteMinderFilter, он ищет заголовок запроса SM_USER, я полагаю, я мог бы изменить его на любой другой заголовок? И это не имя пользователя, это токен ... поэтому я задаюсь вопросом, могу ли я изменить loadUserName, чтобы вместо этого взять токен-строку, а затем сделать вызов OpenAM, чтобы получить имя пользователя ... Как вы думаете, это сработает? – tjholmes66

+0

Да, имя заголовка может быть любым, что вы хотите (primaryRequestHeader). Служба пользовательских данных представляет собой пользовательскую реализацию и может получать данные на основе этого токена. – 6ton

+0

Я только что обновил вопрос для модульного теста. Мне нужно добавить заголовок в конструктор запросов, который не должен быть слишком сложным для выполнения, но при попытке получить сообщение об ошибке, которое имеет значение SecurityContext в моем тесте. Какие-либо советы по правильному тестированию устройства, чтобы убедиться, что это правильно? Благодаря! – tjholmes66