需要你 的User类实现接口:代码如下;
package com.lrq.domain;为了实现用户名密码从数据库后台加载需要手动的编写实现类,以替换硬编码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails {
private static final long serialVersionUID = -1277349007815180913L;
private String id;
private String username;
private String password;
private List<Role> roles = new ArrayList<Role>();
public Collection<? extends GrantedAuthority> getAuthorities() {
//将role转换为GrantedAuthority
List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
GrantedAuthority authority=null;
for(Role role:roles){
authority=new SimpleGrantedAuthority(role.getName());
authorities.add(authority);
}
return authorities;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
比如:
<password-encoder hash="md5">实现类替换上述硬编码;
<salt-source user-property="username"/>
</password-encoder>
<user-service> <!-- 一个项目中只包含一个userservice,一个userservice中包含多个用户 -->
<!-- 以下每个user元 都是一个User类。 -->
<user name="admin" password="9b2f9dd7c60267f3c0f69bc061f6cbaf" authorities="ROLE_USER,ROLE_ADMIN"/>
<user name="jack" password="19d52d94b3215b21f89fe7d4e6b0fd76" authorities="ROLE_USER"/>
</user-service>
package com.lrq.service;为了实现将我们对User类的控制像之前一致,放入session中;我们写了一个监听器,将spring默认帮我们设置的登录标记改变为:
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.lrq.domain.Role;
import com.lrq.domain.User;
public class UserService extends JdbcDaoSupport implements UserDetailsService {
// 据用户名查询用户信息
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// 据用户名查询此用户是否存在
String sql = "select * from user where user_name=?";
ResultSetExtractor<User> rs=new ResultSetExtractor<User>() {
public User extractData(ResultSet rs) throws SQLException,
DataAccessException {
if(rs.next()){
User u = new User();
u.setId(rs.getString("user_id"));
u.setUsername(rs.getString("user_name"));
u.setPassword(rs.getString("user_pwd"));
return u;
}else{
return null;
}
}
};
System.err.println(">:sql is:"+sql);
User user = getJdbcTemplate().query(sql,rs,username);
//在查询用户所拥有的角色
//最后要封装成List<role>对象
if(user!=null){
sql = "SELECT r.role_id,r.role_name "+
"FROM role r INNER JOIN user_role ru ON r.role_id=ru.role_id "+
"WHERE ru.user_id=?";
System.err.println("sql is:"+sql);
List<Role> roles = getJdbcTemplate().query(sql,new ResultSetExtractor<List<Role>>(){
public List<Role> extractData(ResultSet rs)
throws SQLException, DataAccessException {
List<Role> list = new ArrayList<Role>();
while(rs.next()){
Role r = new Role();
r.setId(rs.getString("role_id"));
r.setName(rs.getString("role_name"));
list.add(r);
}
return list;
}
},user.getId());
user.setRoles(roles);//将角色放到User中去
}
System.err.println("用户是::"+user);
return user;
}
}
User,代码如下:
package com.lrq.web.listener;但是需要注意的是:这个监听器的配置可能和spring配置文件中对HttpSession的配置相冲突,到时候碰到问题再解决;
import java.util.Collection;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import com.lrq.domain.User;
public class MySessionAttributeListener implements HttpSessionAttributeList ener {
public void attributeAdded(HttpSessionBindingEvent se) {
// 直接就是session中的属性名称;SPRING_SECURITY_CONTEXT
if (se.getName().equals("SPRING_SECURITY_CONTEXT")) {
// 转成user,放到域中;
User user = (User) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
String username = user.getUsername();
String password = user.getPassword();
System.out.println(username);
System.out.println(password);
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
System.out.println(authorities);
se.getSession().setAttribute("user", user);
}
}
public void attributeRemoved(HttpSessionBindingEvent se) {
}
public void attributeReplaced(HttpSessionBindingEvent se) {
if (se.getName().equals("SPRING_SECURITY_CONTEXT")) {
// 转成user,放到域中;
User user = (User) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
se.getSession().setAttribute("user", user);
}
}
}
Spring中可以配置单点登录:
单点登录的实现思想:后者登录信息覆盖前者,可以用SSL,可以用数据库标示,也可以用监听器结合AJAX实现;
自动登录的实现remember-me配置实现自动登录,记住我,需要在页面提供一个checkbox,并且名称必须为:
<input type='checkbox' name='_spring_security_remember_me'/>
同时角色权限,也不能硬编码,要从数据库后台中读取;我们定义了一个自定义的FilterChain:
需要的配置为:
<bean:bean id="myFilter" class="org.springframework.security.web.access.intercept.FilterSecurityIntercepto这个过滤器接收三个参数:r">
<bean:property name="authenticationManager" ref="authenticationManager"></bean:property>
<bean:property name="accessDecisionManager">
<bean:bean class="org.springframework.security.access.vote.AffirmativeBased">
<bean:property name="decisionVoters">
<bean:list>
<bean:bean class="org.springframework.security.access.vote.RoleVoter"></bean:bean>
</bean:list>
</bean:property>
</bean:bean>
</bean:property>
<bean:property name="securityMetadataSource" ref="dbSecurityMetadataSource"></bean:property>
</bean:bean>
<!-- 定义自己的安全资源 -->
<bean:bean id="dbSecurityMetadataSource" class="com.lrq.service.DBSecurityMetadataSource " init-method="init">
<bean:property name="dataSource" ref="dataSource"></bean:property>
</bean:bean>
authenticationManager:对应于上面的验证管理器,UserService,如果需要md5编 也可以手动实现;代码如下:
accessDecisionManager :角色匹配策略,投票机制有三种,自己 据情况可以 配置;
securityMetadataSource:安全资源信息数据源配置;
是我们写的一个管理器:这个是关键,权限控制的 心就是这个;
package com.lrq.service;init方法负责在服务器启动时,从后台加载所有的资源权限,和对应的角色关系,即那些用户拥有这个权限;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import com.lrq.domain.Privilege;
import com.lrq.domain.Role;
public class DBSecurityMetadataSourceextends JdbcDaoSupport implements FilterInvocationSecurity MetadataSource{
private Map<String,Collection<ConfigAttribute>> resourcesMap=new HashMap<String, Collection<ConfigAttribute>>();
@SuppressWarnings({ "unchecked", "rawtypes" })
public void init() {
// 载所有的权限信息;
String sql = "select privilege_id,privilege_name,privilege_url from privilege";
List<Privilege> privileges = this.getJdbcTemplate().query(sql,
new RowMapper<Privilege>() {
public Privilege mapRow(ResultSet rs, int rowNum)
throws SQLException {
Privilege p = new Privilege();
p.setId(rs.getString("privilege_id"));
p.setName(rs.getString("privilege_name"));
p.setUrl(rs.getString("privilege_url"));
return p;
}
});
// 查询每个权限对应的角色;
sql = "select r.role_id,r.role_name,r.role_desc from role r inner join privilege_role pr on pr.role_id=r.role_id "
+ " where pr.privilege_id=?";
for (Privilege privilege : privileges) {
List<Role> roles = this.getJdbcTemplate().query(sql,
new RowMapper<Role>() {
public Role mapRow(ResultSet rs, int rowNum)
throws SQLException {
Role r = new Role();
r.setDescription(rs.getString("role_desc"));
r.setId(rs.getString("role_id"));
r.setName(rs.getString("role_name"));
return r;
}
}, privilege.getId());
privilege.setRoles(roles);
}
// 将privileges转成 Map<String, Collection<ConfigAttribute>>
// 这个map的key是url,value的ConfigAttribute对象来源于Role的name
Set<ConfigAttribute> config = null; // 必须保证ConfigAttribute的唯一性;
for (Privilege privilege : privileges) {
String url = privilege.getUrl();
config = new HashSet<ConfigAttribute>();
for (Role role : privilege.getRoles()) {
ConfigAttribute attribute = new SecurityConfig(role.getName());
config.add(attribute);
}
resourcesMap.put(url, config);
}
System.out.println("启动完成" + resourcesMap);
}
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException{
System.err.println("所有资源为:"+resourcesMap);
FilterInvocation invo =(FilterInvocation) object;
String url = invo.getRequestUrl();
System.err.println("用spring的方式获取url为:"+url);
url = invo.getRequest().getRequestURI();//获取项目名和请求的路径
System.err.println("自己的方式获取:"+url);
String contextPath = invo.getRequest().getContextPath();
url = url.replace(contextPath,"");
System.err.println("自己处理以后:"+url);
if(url.indexOf(";")!=-1){//说明里面有jsessionid
System.err.println("去掉jsessionid:");
url = url.substring(0,url.indexOf(";"));
}
Collection<ConfigAttribute> attr = resourcesMap.get(url);// 为我们的url是完全匹配模式所以,直接从map中获取即可
return attr;
}
// 载所有的角色信息;
public Collection<ConfigAttribute> getAllConfigAttributes() {
Collection<ConfigAttribute> all = new HashSet<ConfigAttribute>();
//遍历获取所有的资源
for(Map.Entry<String,Collection<ConfigAttribute>> en : resourcesMap.entrySet()){
all.addAll(en.getValue());//一次性部分添
}
System.err.println("获取所有可用的角色信息:"+all);
return all;
}
public boolean supports(Class<?> clazz) {
return true;
}
}
在
getAllConfigAttributes中的逻辑是 载所有的权限对应的集合;到时候用户登录时,主要是和他进行比对;
如果需要对密 采用MD5 密,并以username为盐值,salt:
配置如下: <!-- 定义md5 密对象 -->
声明 密器:
<bean:bean id="md5" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
声明 密策略:
<password-encoder ref="md5" >
<salt-source user-property="username"/>
</password-encoder>
此到时候用户注册的时候,在保存用户密 的时候需要调用上面的 密器类进行 密;否则会出现问题;
<script type="text/javascript" id="wumiiRelatedItems"> </script>