折腾了一天终于把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共同使用的问题(项目中没用到,先记录在这里)
结论是:
[b]与filter mapping的配置顺序有关,spring security3的必须放在sitemesh的前面[/b]
最大的心得是网上的资料只能参考,最重要的文档是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共同使用的问题(项目中没用到,先记录在这里)
结论是:
[b]与filter mapping的配置顺序有关,spring security3的必须放在sitemesh的前面[/b]