gpt4 book ai didi

spring-security - 使用 Spring Security 访问 CAS 发布的属性

转载 作者:行者123 更新时间:2023-12-01 12:31:33 27 4
gpt4 key购买 nike

我很难弄清楚如何使用 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 发布的属性。

环顾四周后,我确实注意到提到了 GrantedAuthorityFromAssertionAttributesUserDetailsS​​ervice 类,所以我更改了我的安全上下文 .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 应用程序中使用多个属性时?

最佳答案

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

例如,我的用户:

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

然后,借用GrantedAuthorityFromAssertionAttributesUserDetailsS​​erviceJdbcDaoImpl的一些结构,我创建了一个MyUserDetailsS​​ervice:

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 中设置了 authenticationUserDetailsS​​ervice 以使用此类,从我的容器(在本例中为 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>
...

关于spring-security - 使用 Spring Security 访问 CAS 发布的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33904009/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com