本文是一个简单的基于用户,角色,权限的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项目了,有很多的具体细节没有讲到,我在此附上源码,有兴趣的可以自己跑跑一下!