问题
I've been doing a lot of research on this, and there are tons of examples, but I am overwhelmed by the choices, but I have put together some code together to do what I want.
I have a Apache Load Balancer which re-directs me to a login page for our OpenAM 10. When I go to https://portal.mydomain.com/myapp I get redirected to: https://sso.mydomain.net:9443/sso/UI/Login?module=AGMAuth&goto=https%3A%2F%2Fvmlb.mydomain.net%3A443%2Fmyapp
This is my login page, I am asked for a username and password, and I get back a token that looks like (AQIC5wM2LY4Sfcyl9raxhA-xBE14_4QBbkjXi-rFn43PMpc.AAJTSQACMDE.) into a cookie. From the front-end, I can get the token from the cookie, I can add it to the request header for every call to my back-end which is a SpringMVC RESTful back-end.
Just an FYI, I know how to call an OpenAM RESTful api, passing in that token and getting back the username and some other information. Roles are not kept in OpenAM. If in the back-end of my RESTful web-services, I call OpenAM with the token ... if I do not get back any data, then I want to go back to my custom authentication login page: https://sso.mydomain.net:9443/sso/UI/Login?module=AGMAuth&goto=https%3A%2F%2Fvmlb.mydomain.net%3A443%2Fmyapp
Here is the controller of one of my secured URL's:
@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;
}
Do I need to have every secure URL try to get get principal?
Now here is my spring-security.XML security file. It may not be correct, but it's where I started:
<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>
Now here is some security code on the back-end. This is the code that goes back to the database to get user details based on the username, and I can also load the roles for this user. I got this from one of the Spring Security 4 examples.
@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;
}
}
Here is another piece of security code that I am not sure if this works for me or not. Since in this case, I am really looking at Pre-Authentication strategies, and I am not sure if any of them really apply in this case. I also see that I can implement a RequestHeaderFilter, but I am also not sure how this fits in.
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;
}
}
So, I am pulling in some code and trying to make sense of how this all works based on the many slight variations of how other people have working solutions. Again, I have a token which is coming from a third-party API and put in a cookie. So, I was looking at remember-me scenarios which absolutely makes sense for me ... except that when the token is not valid, expires, or does not exist, then I am re-directed to that third-party login page. I thought I saw an example of a custom login form that was given a URL, so if authentication was not valid (token either does not exist or is not valid) then we are not authorized and sent back to that custom login url which would work for me.
Based on what I have already added, I should be able to get a username from the token after I pass it to OpenAM ... but do I need a requestheader filter of some sort to get it first ... then use that username to load the userdetails and roles and put that in the SecurityContext?
I'm just a noobie to all this Spring Security piping, so any help with this particular setup would he greatly helpful. And BTW, I don't mind using XML for spring security configuration. If I need to add my web.xml file, I can do that also, but please presume I did setup my web.xml correctly. Thanks!
UPDATED: I have a Unit test that looks like:
@Test
public void testMockGetById() throws Exception
{
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(BASE_URL + "/trialId/149");
this.mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isOk());
}
Based on the SiteMinder example that is suggested below. I am now looking for a request header called "openam_token" and not "SM_USER" I have already tested this without a token, and I get the 500 error that there is no token.
So, now I need to alter my unit test to add a token in the request header. This token will return the correct user who has access to this URL. The second test is to have a token that comes from a different user, one that does not have access to this URL. If my test conditions work, I'd consider this a win.
回答1:
Siteminder authentication works in a similar fashion setting a request header at the load balancer (Apache). I would recommend looking at examples on how siteminder is set up with spring security. See this and spring doc here.
Don't think you need the CustomAuthenticationProvider
since authentication is being handled before requests reach your rest API.
来源:https://stackoverflow.com/questions/33787085/spring-security-4-with-third-party-authentication-token