本文是一个简单的基于用户,角色,权限的spring security应用。
使用步骤:
1 在web.xml引入如下配置:
<listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>
2 增加spring security配置文件spring_security.xml(名称可以自己取):
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http auto-config="false" access-denied-page="/accessDenied.jsp">
<!-- 不要过滤图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录。
<intercept-url pattern="/**/*.jpg" filters="none" />
<intercept-url pattern="/**/*.png" filters="none" />
<intercept-url pattern="/**/*.gif" filters="none" />
<intercept-url pattern="/**/*.css" filters="none" />
<intercept-url pattern="/**/*.js" filters="none" /> -->
<!-- 登录页面和忘记密码页面不过滤 -->
<intercept-url pattern="/login.jsp" filters="none" />
<intercept-url pattern="/jsp/forgotpassword.jsp" filters="none" />
<!-- ROLE_ENTER_ORDINARY_PAGE, -->
<intercept-url pattern="/index.jsp" access="ROLE_ENTER_ORDINARY_PAGE, ROLE_ENTER_HIGH_LEVEL_PAGE" />
<!-- 检测失效的sessionId,超时时定位到另外一个URL, 防止固化session攻击 -->
<!-- <session-management invalid-session-url="/timeout.jsp" session-fixation-protection="migrateSession"> -->
<session-management session-authentication-strategy-ref="sessionAuthenticationStrategy">
</session-management>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<form-login login-page="/login.jsp" authentication-failure-url="/loginError.jsp" default-target-url="/index.jsp"/>
<logout invalidate-session="true" logout-success-url="/login.jsp"/>
<http-basic/>
<!-- <remember-me user-service-ref="userService"/> -->
<remember-me user-service-ref="userService" data-source-ref="myDataSource"/>
<anonymous/>
</http>
<!-- 注意能够为authentication-manager 设置alias别名 -->
<authentication-manager alias="authenticationManager">
<!-- <authentication-provider user-service-ref="userDetailsManager"> -->
<authentication-provider user-service-ref="userService">
<password-encoder hash="md5">
<salt-source user-property="username"/>
</password-encoder>
<!-- <password-encoder ref="passwordEncoder"> -->
<!-- 用户名做为盐值 -->
<!--<salt-source user-property="username" />
</password-encoder>-->
</authentication-provider>
</authentication-manager>
</b:beans>
3 在web.xml中添加spring security配置的引用
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/spring.xml, /WEB-INF/spring/spring_security.xml</param-value>
</context-param>
4 创建用户,角色,权限表
--创建表t_user
create table t_user(
id int PRIMARY key auto_increment,
username VARCHAR(50) not null,
password varchar(50) not NULL
);
--插入数据
insert into t_user values(null, 'user', '47a733d60998c719cf3526ae7d106d13');
insert into t_user values(null, 'admin', 'ceb4f32325eda6142bd65215f4c0f371');
--创建表t_Role
create table (
id int PRIMARY key auto_increment,
name VARCHAR(50) not null
);
--插入数据
insert into t_role values(null, '普通用户');
insert into t_role values(null, '高级用户');
--创建表t_Perm
create table t_perm(
id int PRIMARY key auto_increment,
name VARCHAR(50) not null
);
--插入数据
insert into t_perm values(null, 'ROLE_ENTER_ORDINARY_PAGE');
insert into t_perm values(null, 'ROLE_ENTER_HIGH_LEVEL_PAGE');
--创建表角色--人员的关联关系(多对多)
create table t_user_role(
id int primary key auto_increment,
userid int not null,
roleid int not null
);
--创建约束
alter table t_user_role
add CONSTRAINT fk_userid foreign key (userid)
REFERENCES t_user (id);
alter table t_user_role
add CONSTRAINT fk_roleid foreign key (roleid)
REFERENCES t_role (id);
--插入数据
insert into t_user_role
select null,a.id, b.id from t_user a, t_role b
where a.username = 'user' and b.name='普通用户';
insert into t_user_role
select null,a.id, b.id from t_user a, t_role b
where a.username = 'admin' and b.name='高级用户';
--创建角色--权限关联关系表(多对多)
create table t_role_perm(
id int primary key auto_increment,
permid int not null,
roleid int not null
);
--创建约束
alter table t_role_perm
add CONSTRAINT fk_perm_permid foreign key (permid)
REFERENCES t_perm (id);
alter table t_role_perm
add CONSTRAINT fk_perm_roleid foreign key (roleid)
REFERENCES t_role (id);
--插入数据
insert into t_role_perm
select null,a.id, b.id from t_role a, t_perm b
where a.name = '普通用户' and b.name='ENTER_ORDINARY_PAGE';
insert into t_role_perm
select null,a.id, b.id from t_role a, t_perm b
where a.name = '高级用户' and b.name='ROLE_ENTER_HIGH_LEVEL_PAGE';
5 在spring security的配置文件中,有如下配置:
<authentication-manager alias="authenticationManager"> <!-- <authentication-provider user-service-ref="userDetailsManager"> --> <authentication-provider user-service-ref="userService"> <password-encoder hash="md5"> <salt-source user-property="username"/> </password-encoder> <!-- <password-encoder ref="passwordEncoder"> --> <!-- 用户名做为盐值 --> <!--<salt-source user-property="username" /> </password-encoder>--> </authentication-provider> </authentication-manager>
对于authentication-provider的配置有很多种,可以使用jdbc的配置,也可以使用service的,在此,我们使用service配置来实现用户的登陆和授权验证
6 在spring的配置文件中注册要使用的service
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- 配置占位符 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" >
<value>WEB-INF/config/jdbc.properties</value>
</property>
</bean>
<!-- 数据源 -->
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${JDBC.DRIVERNAME}"/>
<property name="url" value="${JDBC.URL}"/>
<property name="username" value="${JDBC.USERNAME}"/>
<property name="password" value="${JDBC.PASSWORD}"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="packagesToScan">
<list>
<value>main.model</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
hibernate.cache.provider_class=org.hibernate.cache.NoCacheProvider
</value>
</property>
</bean>
<bean id="userDaoImpl" class="main.daoImpl.UserDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"></property>
</bean>
<bean id="passwordEncoder"
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<!-- 用户详细信息管理 : 数据源、用户缓存、启用用户组功能。 -->
<bean id="userDetailsManager"
class="org.springframework.security.provisioning.JdbcUserDetailsManager">
<property name="dataSource" ref="myDataSource" />
<!-- <property name="userCache" ref="userCache" /> -->
</bean>
<bean id="userService" class="main.service.UserService">
<property name="userDaoImpl" ref="userDaoImpl"/>
</bean>
<!-- spring security session-manager -->
<bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry" />
<property name="expiredUrl" value="/timeout.jsp" />
</bean>
<bean id="sessionAuthenticationStrategy"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg type="org.springframework.security.core.session.SessionRegistry" index="0"
ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true"></property>
</bean>
<bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
</beans>
7 使用UserDetailsService做验证和授权,首先要实现UserDetails接口,以把我们从数据库查询到的数据包装后供spring security使用
package main.security;
import java.util.ArrayList;
import java.util.Collection;
import main.model.Perm;
import main.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails;
@SuppressWarnings("serial")
public class UserDetail implements UserDetails {
public UserDetail(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.authorities = new ArrayList<GrantedAuthority>();
for (Perm perm : user.getPerms()) {
this.authorities.add(new GrantedAuthorityImpl(perm.getName()));
}
}
private String username;
private String password;
private Collection<GrantedAuthority> authorities;
public Collection<GrantedAuthority> getAuthorities() {
return this.authorities;
}
public String getPassword() {
return this.password;
}
public String getUsername() {
return this.username;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UserDetail) {
UserDetail another = (UserDetail)obj;
return this.getUsername().equals(another.getUsername());
}
return super.equals(obj);
}
@Override
public int hashCode() {
return this.getUsername().hashCode();
}
public boolean isEnabled() {
return true;
}
}
在UserDetails中可以获取到当前用户的权限
8 实现UserDetailsService接口,实现验证和授权
package main.service;
import java.util.List;
import main.daoImpl.UserDaoImpl;
import main.model.Perm;
import main.model.User;
import main.security.UserDetail;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class UserService implements UserDetailsService {
private UserDaoImpl userDaoImpl;
public void setUserDaoImpl(UserDaoImpl userDaoImpl) {
this.userDaoImpl = userDaoImpl;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
User user = this.userDaoImpl.findByName(username);
if (user == null) {
throw new UsernameNotFoundException("用户名错误!");
}
//从数据库中获取当前用户拥有权限
List<Perm> permList = this.userDaoImpl.listPermByUser(user);
user.setPerms(permList);
return new UserDetail(user);
}
}
看到loadUserByUsername方法时,你可能会疑问,只是在验证用户名是否正确,并没验证密码,是的,此方法只验证用户名,返回值类为UserRetails,该接口有个getPassword()方法,实现该接口时只要把用户的密码在此方法中返回就行了,密码的验证由spring security 自己完成的
到此就是一个完整的基于用户,角色,权限管理的spring security项目了,有很多的具体细节没有讲到,我在此附上源码,有兴趣的可以自己跑跑一下!