权限解决方案:Spring security3.0.…

他适应的是典型的权限-角色-用户方案;
需要你 的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;

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类的控制像之前一致,放入session中;我们写了一个监听器,将spring默认帮我们设置的登录标记改变为:
User,代码如下:
package com.lrq.web.listener;

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 HttpSessionAttributeListener {

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配置文件中对HttpSession的配置相冲突,到时候碰到问题再解决;
Spring中可以配置单点登录:
单点登录的实现思想:后者登录信息覆盖前者,可以用SSL,可以用数据库标示,也可以用监听器结合AJAX实现;
自动登录的实现remember-me配置实现自动登录,记住我,需要在页面提供一个checkbox,并且名称必须为:
<input type='checkbox' name='_spring_security_remember_me'/>
同时角色权限,也不能硬编码,要从数据库后台中读取;我们定义了一个自定义的FilterChain:
需要的配置为:
  <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
 <bean:bean id="myFilter" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<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;

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 DBSecurityMetadataSource extends JdbcDaoSupport implements FilterInvocationSecurityMetadataSource{
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;
}

}

init方法负责在服务器启动时,从后台加载所有的资源权限,和对应的角色关系,即那些用户拥有这个权限;

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>
 
阅读(185) | 评论(0)
推荐 转载
历史上的今天
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值