spring security3的根本配置和使用
spring security3的基本配置和使用
折腾了一天终于把spring security 3.1.0集成进项目中了,这里特别说明了一下版本,因为不同的版本有些用法会存在一些差异。期间遇到了很多问题,记录在这里。
最大的心得是网上的资料只能参考,最重要的文档是spring security自带的tutorial的例子,这里的用法是和你要用到的版本是一致的。
需要补充以下几个问题
1. 注释中提到如果配置了表达式,<intercept-url pattern="/employee/**"access="ROLE_ADMIN" /> 这种形式的配置会报以下异常
java.lang.IllegalArgumentException: Failed to evaluate expression 'ROLE_ADMIN'
org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:50)
org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:204)
解决办法就是如上配置文件改成hasRole的形式。
2. 关于数据库表和spring security不一致的实现。 表结构大致如下
spring security3提供的接口是UserDetails和 UserDetailsService.所以首先需要领域对象中的Employee实现UserDetails, 然后用一个UserDetailsService的实现类去从数据库中获取Employee的信息。 下面是部分代码
3. spring security jsp页面的标签库
4. 在jsp页面中使用<sec:authorization>标签时,如果需要判断多个条件的正确写法
老版本的spring security中有这种用法
这种用法在spring security3中是deprecated.那么怎么写出这种等价的用法,经过试验,下面这种写法可以工作
注意配置文件中必须设置use-expressions为true,这在spring security3的文档中说明了的
5. spring security3和sitemesh共同使用的问题(项目中没用到,先记录在这里)
结论是:
与filter mapping的配置顺序有关,spring security3的必须放在sitemesh的前面
折腾了一天终于把spring security 3.1.0集成进项目中了,这里特别说明了一下版本,因为不同的版本有些用法会存在一些差异。期间遇到了很多问题,记录在这里。
最大的心得是网上的资料只能参考,最重要的文档是spring security自带的tutorial的例子,这里的用法是和你要用到的版本是一致的。
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="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.1.xsd"> <!--网上有些版本的用法是把这些配置和intercept-url配置放在一起,并且是filters=none,这种配置这个版本不支持--> <!-- 不要过滤图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录。--> <http pattern="/**/*.jpg" security="none" /> <http pattern="/**/*.png" security="none" /> <http pattern="/**/*.gif" security="none" /> <http pattern="/**/*.css" security="none" /> <http pattern="/**/*.js" security="none" /> <!-- 登录页面不过滤 --> <http pattern="/login.jsp" security="none" /> <!--网上会看到很多access="ROLE_XXX"的配置,这是因为没有用到表达式,即use-expressions没有配置 另外一个问题就是一旦用了表达式,上面这种配置就不能工作,会报非法参数异常。 必须改成hasRole这种 模式的配置 --> <http auto-config="true" use-expressions="true" access-denied-page="/error.jsp"> <intercept-url pattern="/main/**" access="isAuthenticated()" /> <!--注意角色的命名也必须是ROLE_前缀开头,好像可以配置,但是我没找到--> <intercept-url pattern="/employee/**" access="hasRole('ROLE_ADMIN')" /> <!--form-login配置了以后spring security会处理authentication的过程, 但注意这里的一些命名约定 前端jsp的form表单的submit的地址(j_spring_security_check可以配置),用户名j_username和密码j_password的命名等。这个都可以在tutorial的例子里面找到 --> <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/main/mainframe.do" /> <!--logout配置基本上跟login的差不多,这里logour-url可以不配,默认的是j_spring_security_logout--> <logout logout-success-url="/login.jsp" delete-cookies="JSESSIONID" logout-url="/logout.do"/> <remember-me /> <!-- session超时后重定向的登陆页面 --> <session-management invalid-session-url="/login.jsp" /> </http> <!-- Usernames/Passwords are rod/koala dianne/emu scott/wombat peter/opal --> <!-- 注意能够为authentication-manager 设置alias别名,这里因为我的表结构和spring security提供的 不一样,所以需要重新实现--> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userDetailsManager"> </authentication-provider> </authentication-manager> </beans:beans>
需要补充以下几个问题
1. 注释中提到如果配置了表达式,<intercept-url pattern="/employee/**"access="ROLE_ADMIN" /> 这种形式的配置会报以下异常
java.lang.IllegalArgumentException: Failed to evaluate expression 'ROLE_ADMIN'
org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:50)
org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:204)
解决办法就是如上配置文件改成hasRole的形式。
2. 关于数据库表和spring security不一致的实现。 表结构大致如下
/*员工角色*/ create table if not exists myrole ( roleid int unsigned not null auto_increment, rolename varchar(10) not null, issystem bit not null default 0, authorities text, primary key (roleid) )engine=innodb default charset=utf8; /*员工基本信息*/ create table if not exists employee ( username varchar(30) not null, password varchar(30) not null, realname varchar(30), cellphone varchar(20), email varchar(30), status bit not null, primary key (username) )engine=innodb default charset=utf8; /*员工角色对应表*/ create table if not exists employee_role_conn ( username varchar(30) not null, roleid int unsigned not null, primary key (username, roleid), foreign key (username) references employee(username), foreign key (roleid) references myrole(roleid) )engine=innodb default charset=utf8;
spring security3提供的接口是UserDetails和 UserDetailsService.所以首先需要领域对象中的Employee实现UserDetails, 然后用一个UserDetailsService的实现类去从数据库中获取Employee的信息。 下面是部分代码
public class Employee implements UserDetails, Serializable { private String username; private String password; private String realname; private String cellphone; private String email; private boolean status; //是否离职 true 在职 false 离职 private List<EmployeeRole> roles; private Set<GrantedAuthority> authorities;// 所有的权限信息 } public class UserDetailServiceImpl implements UserDetailsService { private EmployeeMapper employeeMapper; @Resource(name = "employeeMapper") public void setEmployeeMapper(EmployeeMapper employeeMapper) { this.employeeMapper = employeeMapper; } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Employee admin = null; try { admin = employeeMapper.getEmployeeById(username); } catch (Exception ex) { Logger.getLogger(UserDetailServiceImpl.class.getName()).log(Level.SEVERE, null, ex); } if (admin == null) { throw new UsernameNotFoundException("管理员[" + username + "]不存在!"); } admin.setAuthorities(getGrantedAuthorities(admin)); return admin; } private Set<GrantedAuthority> getGrantedAuthorities(Employee admin) { Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>(); for (EmployeeRole role : admin.getRoles()) { for (String authority : role.getAuthorityList()) { grantedAuthorities.add(new SimpleGrantedAuthority(authority)); } } return grantedAuthorities; } }
3. spring security jsp页面的标签库
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
4. 在jsp页面中使用<sec:authorization>标签时,如果需要判断多个条件的正确写法
老版本的spring security中有这种用法
<sec:authorize ifAnyGranted="ROLE_XXX,ROLE_YYY">
这种用法在spring security3中是deprecated.那么怎么写出这种等价的用法,经过试验,下面这种写法可以工作
<sec:authorize access="hasRole('ROLE_XXX) OR hasRole('ROLE_YYY')">
注意配置文件中必须设置use-expressions为true,这在spring security3的文档中说明了的
5. spring security3和sitemesh共同使用的问题(项目中没用到,先记录在这里)
结论是:
与filter mapping的配置顺序有关,spring security3的必须放在sitemesh的前面