Aet*_*lus 3 cas spring-security
我很难弄清楚如何使用 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");
// ...
}
Run Code Online (Sandbox Code Playgroud)
当使用 Spring MVC 但没有 Spring Security 创建 servlet 时,访问属性似乎基本没有区别:
@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");
// ...
}
Run Code Online (Sandbox Code Playgroud)
但是,在实现 Spring Security 后,request.getUserPrincipal()返回 aCasAuthenticationToken而不是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>
Run Code Online (Sandbox Code Playgroud)
到
<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>
Run Code Online (Sandbox Code Playgroud)
然后,通过一种更加迂回的方法,我可以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();
// ...
}
Run Code Online (Sandbox Code Playgroud)
然而,除了比以前的实现稍微长一点之外,它似乎不可能支持从 CAS 映射多个属性(例如,如果 CAS 也释放firstName和lastName属性)。
是否有更好的方法来设置安全上下文 .xml 以允许更轻松地访问这些属性,特别是如果我想在 Web 应用程序中使用多个属性?
我想我明白了。除了将属性设置为权限之外(如果您使用这些属性来确定权限(即hasAuthority('username')),这可能很有用),似乎唯一的其他方法是构造您自己的UserDetails类UserDetailsService。
例如,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;
}
}
Run Code Online (Sandbox Code Playgroud)
GrantedAuthorityFromAssertionAttributesUserDetailsService然后,借用和的一些结构JdbcDaoImpl,我创建了一个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);
}
}
Run Code Online (Sandbox Code Playgroud)
authenticationUserDetailsService最后,我在 my 中设置casAuthenticationProvider使用此类,从我的容器(本例中为 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>
...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2335 次 |
| 最近记录: |