Spring 具有第三方身份验证令牌的安全 4
Spring Security 4 with third party authentication token
我已经对此进行了大量研究,并且有大量示例,但我对这些选择感到不知所措,但我已经将一些代码放在一起来完成我想要的。
我有一个 Apache 负载均衡器,它 re-directs 我到 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.) 的令牌一块饼干。从 front-end,我可以从 cookie 中获取令牌,我可以将它添加到请求 header,以便每次调用我的 back-end,这是一个 SpringMVC RESTful back-end.
仅供参考,我知道如何调用 OpenAM RESTful api,传递该令牌并取回用户名和一些其他信息。角色不保留在 OpenAM 中。如果在 RESTful web-services 的 back-end 中,我使用令牌调用 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>
现在这里是 back-end 上的一些安全代码。这是返回数据库以根据用户名获取用户详细信息的代码,我还可以为该用户加载角色。我从 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;
}
}
这是另一段安全代码,我不确定它是否适合我。因为在这种情况下,我真的在研究 Pre-Authentication 策略,我不确定它们是否真的适用于这种情况。我还看到我可以实现 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;
}
}
因此,我正在提取一些代码,并试图根据其他人的工作解决方案的许多细微差异来理解这一切是如何工作的。
同样,我有一个来自 third-party API 并放入 cookie 的令牌。所以,我正在研究 remember-me 对我来说绝对有意义的场景......除了当令牌无效、过期或不存在时,我 re-directed 到那个 third-party 登录页面。我以为我看到了一个自定义登录表单的示例,该表单被赋予了 URL,因此如果身份验证无效(令牌不存在或无效),那么我们未获得授权并返回到该自定义登录url 这对我有用。
根据我已经添加的内容,我应该能够在将令牌传递给 OpenAM 后从令牌中获取用户名...但是我是否需要某种请求header 过滤器才能获取它首先...然后使用该用户名加载用户详细信息和角色并将其放入 SecurityContext?
我对所有这些 Spring 安全管道一窍不通,所以对这个特定设置的任何帮助都会对他有很大帮助。顺便说一句,我不介意使用 XML 作为 spring 安全配置。如果我需要添加我的 web.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" 的请求 header 我已经在没有令牌的情况下测试了它,但我收到 500 错误,指出没有令牌。
所以,现在我需要更改单元测试以在请求中添加令牌 header。此令牌将 return 有权访问此 URL 的正确用户。第二个测试是使用来自不同用户的令牌,该用户无权访问此 URL。如果我的测试条件有效,我会认为这是一场胜利。
我已经对此进行了大量研究,并且有大量示例,但我对这些选择感到不知所措,但我已经将一些代码放在一起来完成我想要的。
我有一个 Apache 负载均衡器,它 re-directs 我到 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.) 的令牌一块饼干。从 front-end,我可以从 cookie 中获取令牌,我可以将它添加到请求 header,以便每次调用我的 back-end,这是一个 SpringMVC RESTful back-end.
仅供参考,我知道如何调用 OpenAM RESTful api,传递该令牌并取回用户名和一些其他信息。角色不保留在 OpenAM 中。如果在 RESTful web-services 的 back-end 中,我使用令牌调用 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>
现在这里是 back-end 上的一些安全代码。这是返回数据库以根据用户名获取用户详细信息的代码,我还可以为该用户加载角色。我从 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;
}
}
这是另一段安全代码,我不确定它是否适合我。因为在这种情况下,我真的在研究 Pre-Authentication 策略,我不确定它们是否真的适用于这种情况。我还看到我可以实现 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;
}
}
因此,我正在提取一些代码,并试图根据其他人的工作解决方案的许多细微差异来理解这一切是如何工作的。 同样,我有一个来自 third-party API 并放入 cookie 的令牌。所以,我正在研究 remember-me 对我来说绝对有意义的场景......除了当令牌无效、过期或不存在时,我 re-directed 到那个 third-party 登录页面。我以为我看到了一个自定义登录表单的示例,该表单被赋予了 URL,因此如果身份验证无效(令牌不存在或无效),那么我们未获得授权并返回到该自定义登录url 这对我有用。
根据我已经添加的内容,我应该能够在将令牌传递给 OpenAM 后从令牌中获取用户名...但是我是否需要某种请求header 过滤器才能获取它首先...然后使用该用户名加载用户详细信息和角色并将其放入 SecurityContext?
我对所有这些 Spring 安全管道一窍不通,所以对这个特定设置的任何帮助都会对他有很大帮助。顺便说一句,我不介意使用 XML 作为 spring 安全配置。如果我需要添加我的 web.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" 的请求 header 我已经在没有令牌的情况下测试了它,但我收到 500 错误,指出没有令牌。
所以,现在我需要更改单元测试以在请求中添加令牌 header。此令牌将 return 有权访问此 URL 的正确用户。第二个测试是使用来自不同用户的令牌,该用户无权访问此 URL。如果我的测试条件有效,我会认为这是一场胜利。