spring security3的基本配置和使用

折腾了一天终于把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的前面

猜你喜欢

转载自pwc-beyond.iteye.com/blog/1259265