【问题标题】:Accessing CAS Released Attributes Using Spring Security使用 Spring Security 访问 CAS 发布的属性
【发布时间】:2016-02-27 12:07:34
【问题描述】:

我很难弄清楚如何使用 Spring Security 和 Spring MVC 在 servlet 中访问 CAS 发布的属性。传统上,在没有 Spring 的实现中,我会做这样的事情

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
   // Gets the user ID from CAS
   AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
   final Map<String, Object> attributes = principal.getAttributes();
   String userId = (String) attributes.get("userid");

   // ...
}

使用 Spring MVC 创建 servlet,但没有 Spring Security 时,访问属性似乎基本没有区别:

@RequestMapping("/")
public String welcome(HttpServletRequest request)
{
   // Get the user ID from CAS
   AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();;
   final Map<String, Object> attributes = principal.getAttributes();
   userId = (String) attributes.get("userid");

   // ...
}

但是,在实现 Spring Security 之后,request.getUserPrincipal() 返回一个 CasAuthenticationToken 而不是 AttributePrincipal。据我所知,其中没有任何可检索的对象和数据包含任何 CAS 发布的属性。

环顾四周后,我确实注意到提到了GrantedAuthorityFromAssertionAttributesUserDetailsService 类,所以我将我的安全上下文.xml 从

<security:user-service id="userService">
  <security:user name="user" password="user" authorities="ROLE_ADMIN,ROLE_USER" />
</security:user-service>

<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
  <property name="authenticationUserDetailsService">
    <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
      <constructor-arg ref="userService" />
    </bean>
  </property>
  <property name="serviceProperties" ref="serviceProperties" />
  <property name="ticketValidator">
    <bean class="org.jasig.cas.client.validation.Saml11TicketValidator">
      <constructor-arg value="https://localhost:8443/cas" />
    </bean>
  </property>
  <property name="key" value="casAuthProviderKey" />
</bean>

<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
  <property name="authenticationUserDetailsService">
    <bean class="org.springframework.security.cas.userdetails.GrantedAuthorityFromAssertionAttributesUserDetailsService">
      <constructor-arg>
        <list>
          <value>userid</value>
        </list>
      </constructor-arg>
    </bean>
  </property>
  <property name="serviceProperties" ref="serviceProperties" />
  <property name="ticketValidator">
    <bean class="org.jasig.cas.client.validation.Saml11TicketValidator">
      <constructor-arg value="https://localhost:8443/cas" />
    </bean>
  </property>
  <property name="key" value="casAuthProviderKey" />
</bean>

然后,通过一种更加迂回的方法,我可以通过执行以下操作来访问userid 属性:

@RequestMapping("/")
public String welcome(HttpServletRequest request)
{
   CasAuthenticationToken principal = (CasAuthenticationToken) request.getUserPrincipal();
   UserDetails userDetails = principal.getUserDetails();
   Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) userDetails.getAuthorities();
   Iterator<SimpleGrantedAuthority> it = authorities.iterator();

   String userid = it.next().getAuthority();

   // ...
}

但是,除了比以前的实现稍微长一点之外,它似乎也不可能支持从 CAS 映射多个属性(例如,如果 CAS 还发布了firstNamelastName 属性)。

是否有更好的方法来设置安全上下文 .xml 以允许更轻松地访问这些属性,尤其是当我想在 Web 应用程序中使用多个属性时?

【问题讨论】:

    标签: spring-security cas


    【解决方案1】:

    我想我明白了。除了将属性设置为权限(如果您使用它们来确定权限(即hasAuthority('username'))可能很有用)之外,似乎唯一的另一种方法是构建您自己的UserDetailsUserDetailsService 类。

    例如MyUser:

    package my.custom.springframework.security.userdetails;
    
    import java.util.Collection;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    
    public class MyUser extends User
    {
       private static final long serialVersionUID = 1L;
    
       private String id;
       private String lastName;
       private String firstName;
    
       public MyUser(
             String username,
             String password,
             String id,
             String lastName,
             String firstName,
             Collection<? extends GrantedAuthority> authorities)
       {
          super(username, password, authorities);
          this.id = id;
          this.lastName = lastName;
          this.firstName = firstName;
       }
    
       public String getId()
       {
          return id;
       }
    
       public String getLastName()
       {
          return lastName;
       }
    
       public String getFirstName()
       {
          return firstName;
       }
    }
    

    然后,借用GrantedAuthorityFromAssertionAttributesUserDetailsServiceJdbcDaoImpl的一些结构,我创建了MyUserDetailsService

    package my.custom.springframework.security.userdetails;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    import javax.sql.DataSource;
    
    import org.jasig.cas.client.authentication.AttributePrincipal;
    import org.jasig.cas.client.validation.Assertion;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    import org.springframework.security.cas.userdetails.AbstractCasAssertionUserDetailsService;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    
    public final class MyUserDetailsService extends AbstractCasAssertionUserDetailsService
    {
       public static final String DEF_USERS_BY_ID_QUERY = "select ?, id, last_name, first_name " +
             "from users " + "where id = ?";
       public static final String DEF_AUTHORITIES_BY_ID_QUERY = "select role " +
             "from roles join users on users.username = roles.username " +
             "where users.id = ?";
    
       private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
    
       private JdbcTemplate jdbcTemplate;
    
       private String usersByIdQuery;
       private String authoritiesByIdQuery;
    
       public MyUserDetailsService(DataSource dataSource)
       {
          this.jdbcTemplate = new JdbcTemplate(dataSource);
          this.usersByIdQuery = DEF_USERS_BY_ID_QUERY;
          this.authoritiesByIdQuery = DEF_AUTHORITIES_BY_ID_QUERY;
       }
    
       protected MyUser loadUserDetails(Assertion assertion)
       {
          AttributePrincipal attributePrincipal = assertion.getPrincipal();
          String username = attributePrincipal.getName();
          String id = (String) attributePrincipal.getAttributes().get("userid");
    
          MyUser user = loadUser(username, id);
    
          Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();
          dbAuthsSet.addAll(loadUserAuthorities(id));
          List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);
    
          return createMyUser(username, user, dbAuths);
       }
    
       protected MyUser loadUser(String username, String id)
       {
          return jdbcTemplate.queryForObject(usersByIdQuery, new String[] { username, id },
                new RowMapper<MyUser>()
                {
                   public MyUser mapRow(ResultSet rs, int rowNum) throws SQLException
                   {
                      String username = rs.getString(1);
                      String id = rs.getString(2);
                      String lastName = rs.getString(3);
                      String firstName = rs.getString(4);
                      return new MyUser(username, NON_EXISTENT_PASSWORD_VALUE, id, lastName, firstName,
                            AuthorityUtils.NO_AUTHORITIES);
                   }
                });
       }
    
       protected List<GrantedAuthority> loadUserAuthorities(String id)
       {
          return jdbcTemplate.query(authoritiesByIdQuery, new String[] { id },
                new RowMapper<GrantedAuthority>()
                {
                   public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException
                   {
                      // TODO Replace with rolePrefix variable
                      String roleName = "ROLE_" + rs.getString(1);
                      return new SimpleGrantedAuthority(roleName);
                   }
                });
       }
    
       protected MyUser createMyUser(String username,
             MyUser userFromUserQuery, List<GrantedAuthority> combinedAuthorities)
       {
          return new MyUser(username, userFromUserQuery.getPassword(),
                userFromUserQuery.getId(), userFromUserQuery.getLastName(), userFromUserQuery.getFirstName(),
                combinedAuthorities);
       }
    }
    

    最后,我在我的casAuthenticationProvider 中设置authenticationUserDetailsService 以使用此类,从我的容器(在本例中为Tomcat 6)传入一个全局数据源:

    ...
    <property name="authenticationUserDetailsService">
      <bean class="my.custom.springframework.security.userdetails.MyUserDetailsService">
        <constructor-arg>
          <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/my/conn"/>
        </constructor-arg>
      </bean>
    </property>
    ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-25
      • 2019-02-12
      • 2012-12-02
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 2015-12-28
      相关资源
      最近更新 更多