UserServiceImpl:
@Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException{
String userID = credential.getNameID().getValue();
logger.info(userID + " is logged in");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
authorities.add(authority);
List<Attribute> userAttributes = credential.getAttributes();
logger.info("Credential attributes: " + userAttributes);
for (int attrIndex = 0; attrIndex < userAttributes.size(); attrIndex++) {
Attribute attr = userAttributes.get(attrIndex);
List<XMLObject> attrValues = attr.getAttributeValues();
StringBuilder strBuilder = new StringBuilder();
for (int attrValueIndex = 0; attrValueIndex < attrValues.size(); attrValueIndex++) {
XMLObject currObj = attrValues.get(attrValueIndex);
strBuilder.append(currObj.toString()).append(",");
}
strBuilder.deleteCharAt(strBuilder.length() - 1);
logger.info(attr.getFriendlyName() + ", " + strBuilder.toString());
String samlAttrValue = strBuilder.toString();
switch (attr.getFriendlyName()) {
case "userName":
samlUserAttribute.setUserName(samlAttrValue);
case "email":
samlUserAttribute.setEmail(samlAttrValue);
break;
case "firstName":
samlUserAttribute.setFirstName(samlAttrValue);
break;
case "lastName":
samlUserAttribute.setLastName(samlAttrValue);
break;
case "userType":
samlUserAttribute.setUserType(samlAttrValue);
break;
case "accountName":
samlUserAttribute.setAccountName(samlAttrValue);
break;
case "contactId":
samlUserAttribute.setContactId(samlAttrValue);
break;
default:
logger.info("invalid attribute name" + attr.getFriendlyName());
}
}
logger.info("User details obtained: " + samlUserAttribute);
return new SamlUserDTO(userID, "<abc123>", authorities, samlUserAttribute);
}
UserDetails实施:
private SamlUserAttribute currentUserAttribute;
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
.. setter/getter/contructors for userDetails...
3.Security-config详细信息
<security:http entry-point-ref="samlEntryPoint" access-decision-manager-ref="accessDecisionManager" authentication-manager-ref="authenticationManager"
use-expressions="true">
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/**"
access="isFullyAuthenticated()" />
<security:custom-filter after="BASIC_AUTH_FILTER"
ref="samlFilter" />
<security:csrf disabled="true"/>
</security:http>
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/**"
filters="samlEntryPoint" />
</security:filter-chain-map>
</bean>
<bean id="samlAuthenticationProvider"
class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="UserServiceImpl" />
<property name="forcePrincipalAsString" value="false" />
</bean>
<bean id="UserServiceImpl"
class="com.akamai.marketplace.service.impl.common.UserServiceImpl">
</bean>
在点击所需的URL后,我可以在浏览器上看到IDP重定向发生,IDP回复所需的断言,登陆URL为/ login 但是在我的/ login控制器中,身份验证对象的UserDetail尚未填充自定义数据。
/登录控制器:
@RequestMapping(path="/login",method = RequestMethod.POST)
public ResponseEntity<SamlUserDTO> login() {
logger.info("login API reached through IdP.");
Authentication userAuthentication = SecurityContextHolder.getContext().getAuthentication();
logger.info("user details: "+userAuthentication.getDetails());
logger.info("user credentials: "+userAuthentication.getCredentials());
logger.info("principal " + userAuthentication.getPrincipal());
SamlUserDTO samlUserDTO1 = (SamlUserDTO) userAuthentication.getPrincipal();
return ResponseEntity.ok(samlUserDTO1);
}
日志:
2016-08-11 17:22:16 DEBUG BaseMessageEncoder:56 - 成功编码的消息。 2016-08-11 17:22:16 DEBUG HttpSessionStorage:93 - 将消息a399ehchh04afi304hih7e49fd791g2存储到session someValue
2016-08-11 17:22:16 INFO SAMLDefaultLogger:127 - AuthNRequest; SUCCESS; someIP; SP-entityId; Idp-entityId ;;;
2016-08-11 17:22:37 INFO UserController:18 - 通过IdP到达登录API。
2016-08-11 17:22:37 INFO UserController:20 - 用户详细信息:org.springframework.security.web.authentication.WebAuthenticationDetails@ffffa64e:RemoteIpAddress:Idp-IpAddress; SessionId:someValue
2016-08-11 17:22:37 INFO UserController:21 - 用户凭据:
2016-08-11 17:22:37 INFO UserController:22 - 主要匿名用户 2016-08-11 17:22:37 DEBUG ExceptionHandlerExceptionResolver:133 - 解析处理程序中的异常[public org.springframework.http.ResponseEntity com.akamai.marketplace.controller.UserController.login()]:java.lang.ClassCastException:java .lang.String无法强制转换为controller.model.SamlUserDTO
2016-08-11 17:22:37 DEBUG ExceptionHandlerExceptionResolver:361 - 调用@ExceptionHandler方法:public org.springframework.http.ResponseEntity RESTExceptionHandler.handleExeption(java.lang.Exception) 2016-08-11 17:22:37错误RESTExceptionHandler:60 - 异常处理程序捕获的异常: java.lang.ClassCastException:java.lang.String无法强制转换为package.SamlUserDTO
答案 0 :(得分:1)
这个问题得到了解决。对于面临类似问题的人,即使你已经覆盖了loadBySAML()方法,authNResponses也没有被解码,请查看安全配置文件中的这个xml标签:
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
此过滤器的构造函数arg应该是IdP将发布authNResponse的相对URL。即SP暴露给IdP的终点。 在我的例子中的例子:
<constructor-arg>
<value type="java.lang.String">/login</value>
</constructor-arg>
如果未明确提及此路径,则默认值为
public static final String FILTER_URL = "/saml/SSO";
因此,SAMLProcessorImpl将侦听路径/ saml / SSO以获取SAML消息。
希望这有帮助!