【问题标题】:SAML AuthnResponse not being decoded after successful authentication成功认证后未解码 SAML AuthnResponse
【发布时间】:2016-12-18 14:21:00
【问题描述】:
  1. 用户服务实现:

    @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: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 尚未填充自定义数据。

  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);
    }
    

日志:

2016-08-11 17:22:16 调试 BaseMessageEncoder:56 - 成功编码消息。 2016-08-11 17:22:16 调试 HttpSessionStorage:93 - 将消息 a399ehchh04afi304hih7e49fd791g2 存储到会话 someValue

2016-08-11 17:22:16 信息 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:一些值

2016-08-11 17:22:37 INFO UserController:21 - 用户凭据:

2016-08-11 17:22:37 INFO UserController:22 - 主体anonymousUser 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 调试 ExceptionHandlerExceptionResolver:361 - 调用 @ExceptionHandler 方法:公共 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

【问题讨论】:

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


    【解决方案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 消息。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2017-10-25
      • 2016-10-06
      • 2018-07-23
      • 1970-01-01
      • 2021-07-30
      • 1970-01-01
      • 1970-01-01
      • 2020-12-06
      • 1970-01-01
      相关资源
      最近更新 更多