SAML AuthnResponse在成功验证后未被解码

时间:2016-08-11 18:02:32

标签: spring-boot saml-2.0 spring-saml

  1. 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);
    }
    
  2. 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>
    
  3. 在点击所需的URL后,我可以在浏览器上看到IDP重定向发生,IDP回复所需的断言,登陆URL为/ login 但是在我的/ login控制器中,身份验证对象的UserDetail尚未填充自定义数据。

    1. /登录控制器:

      @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);
      }
      
    2. 日志:

        

      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

1 个答案:

答案 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消息。

希望这有帮助!