spring-security+spring-session配置

spring-session的配置

1.dependency
2.applicationContext.xml
3.web.xml
4.分布式
5.遇到的一些问题

1. dependency

spring-session提供了一个集成的jar包,只需要导入这一个就可以了.


    <dependency>
      <groupId>org.springframework.sessiongroupId>
      <artifactId>spring-session-data-redisartifactId>
      <version>1.2.1.RELEASEversion>
    dependency>

该pom配置对应的jar包是空的,会导入以下一些依赖,你也可以直接引用以下依赖

<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.4.2version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
<version>1.7.1.RELEASEversion>
<scope>compilescope>
<exclusions>
<exclusion>
<artifactId>slf4j-apiartifactId>
<groupId>org.slf4jgroupId>
exclusion>
<exclusion>
<artifactId>jcl-over-slf4jartifactId>
<groupId>org.slf4jgroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-sessionartifactId>
<version>1.2.1.RELEASEversion>
<scope>compilescope>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.8.1version>
<scope>compilescope>
dependency>

有这些依赖就可以安心进行下一步了

2. applicationContext.xml

首先贴出applicationContext-security.xml的代码

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:s="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <s:http  entry-point-ref="restAuthenticationEntryPoint"    >
        <s:csrf disabled="true"/>
        <s:form-login authentication-success-handler-ref="mySuccessHandler" authentication-failure-handler-ref="myFailureHandler"  default-target-url="/t3.html" />
        <s:logout delete-cookies="JSESSIONID"/>
    
        <s:intercept-url pattern="/login*"   access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <s:intercept-url pattern="/secure/**"  access="permitAll"/>
        <s:intercept-url pattern="/admin/**" access="hasRole('admin')"  />
        <s:intercept-url pattern="/**" access="authenticated"/>
    s:http>

    <bean id="mySuccessHandler" class="com.xx.xx.security.RestAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/index.html"/>
    bean>
    <bean id="myFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>
    
    <s:authentication-manager>
        <s:authentication-provider ref='secondLdapProvider' />
    s:authentication-manager>

    
    <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
        <constructor-arg value="ldap://127.0.0.1:389/dc=xxx,dc=com"/>
        <property name="userDn" value="xxx">property>
        <property name="password" value="xxx">property>
    bean>

    <bean id="customJdbcUserDetailsService" class="com.xx.xx.security.CustomJdbcUserDetailsService">
        <property name="dataSource" ref="dataSource"/>
    bean>
    <bean id="customAuthoritiesPopulator" class="com.xx.xx.security.CustomAuthoritiesPopulator">
        <constructor-arg ref="customJdbcUserDetailsService">constructor-arg>
    bean>
    <bean id="secondLdapProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
        <constructor-arg>
            <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
                <constructor-arg ref="contextSource" />
                <property name="userSearch">
                    <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
                      <constructor-arg index="0" value="OU=xxx"/>
                      <constructor-arg index="1" value="(sAMAccountName={0})"/>
                      <constructor-arg index="2" ref="contextSource" />
                    bean>
                property>
            bean>
        constructor-arg>
        <constructor-arg ref="customAuthoritiesPopulator" />
    bean>
    <s:global-method-security secured-annotations="enabled" />



    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
    <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/>

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" />
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"      >
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}" />
    
        <property name="poolConfig" ref="poolConfig" />
    bean>
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="600">property>
    bean>
    <bean class="org.springframework.session.web.http.DefaultCookieSerializer">
        <property name="cookieName" value="JSESSIONID">property>
        <property name="cookiePath" value="/">property>
        
        <property name="domainNamePattern" value="^.+?\.(\w+\.[a-z]+)$">property>
    bean>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://1127.0.0.1:3306/xxx"/>
        <property name="username" value="xxx"/>
        <property name="password" value="xxx"/>
    bean>
beans>

整合spring-security与spring-session之前,原security的配置保留.因为上一篇security的内容只用登录认证,并没有权限,而在实际项目中,认证使用的ldap,而权限则是从数据库里取的.

2.1数据库获取权限数据 (customAuthoritiesPopulator)

这一步和spring-session关系不大,算是spring-security的延伸.在认证成功之后,LdapAuthenticationProvider 会尝试通过调用配置过的LdapAuthoritiesPopulator加载一系列的用户权限.DefaultLdapAuthoritiesPopulator是它的一个实现类.
你可以通过实现 UserDetailsService 接口来定制认证, JdbcDaoImpl 是它的一个jdbc的实现.在本项目,因为要从数据库获取角色.所以创建了 JdbcDaoImpl 的子类CustomJdbcUserDetailsService.这里重写了加载用户权限的方法loadUserAuthorities(String username).
本例中,由于要从数据库加载权限,所以用CustomAuthoritiesPopulator实现该接口的getGrantedAuthorities(DirContextOperations user, String username) 方法.该方法返回 customJdbcUserDetailsService.loadUserAuthorities(username)的返回值.这样就可以获取数据库里的权限数据了.这里需要注意的是loadUserAuthorities(username)protected的方法,所以自己定义的这两个类要在同一个包下才有权限调用.
数据库的权限表,authority内容默认要带ROLE_,不然应用会不认

REATE TABLE `authorities` (
  `username` varchar(255) DEFAULT NULL,
  `authority` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

因为sql都是在JdbcDaoImpl里,其实也可以自己定义,只要把对应的方法写对就行了
通过设置access的值,给url设置权限在这里可以不用ROLE_前缀.

    @Secured("ROLE_ADMIN")
    public Object test(String v)
    {
        return "v="+v;
    }

通过给方法加@Secured注解,也可以实现方法级别的权限控制.


自定义的两个类

public class CustomJdbcUserDetailsService extends JdbcDaoImpl
{
    @Override
    protected List loadUserAuthorities(String username)
    {
        return super.loadUserAuthorities(username);
    }
}
public class CustomAuthoritiesPopulator implements LdapAuthoritiesPopulator
{
    private static Logger logger = LoggerFactory.getLogger(CustomAuthoritiesPopulator.class);

    private CustomJdbcUserDetailsService customJdbcUserDetailsService;

    public CustomAuthoritiesPopulator(CustomJdbcUserDetailsService customJdbcUserDetailsService)
    {
        this.customJdbcUserDetailsService = customJdbcUserDetailsService;
    }

    public Collection getGrantedAuthorities(DirContextOperations user, String username)
    {

        return customJdbcUserDetailsService.loadUserAuthorities(username);
    }
}

2.2redis连接配置

redis连接把对应参数配置好,在定义一个最大闲置时间间隔,就可以用了

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" />
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"      >
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}" />
    
        <property name="poolConfig" ref="poolConfig" />
    bean>
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="600">property>
    bean>

2.3定制cookie

因为后端是有多个应用的,默认cookie的path是app/,所以要修改成/.这里还可以定制认证cookie的名字.spring-session默认的是session,我们这里依据spring-security,还是继续使用JESSIONID.

<bean class="org.springframework.session.web.http.DefaultCookieSerializer">
        <property name="cookieName" value="JSESSIONID">property>
        <property name="cookiePath" value="/">property>
        
        <property name="domainNamePattern" value="^.+?\.(\w+\.[a-z]+)$">property>
    bean>

3.web.xml

web.xml里面只要增加一个filter,这里需要注意的是security和session使用的是同一个filter-class的类,但是作用不一样,所以都要写,而且springSecurityFilterChain的filter-mapping要在下.最后是org.springframework.web.context.ContextLoaderListener加载文件

  <filter>
    <filter-name>springSecurityFilterChainfilter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
  filter>
  <filter>
    <filter-name>springSessionRepositoryFilterfilter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
  filter>

  <filter-mapping>
    <filter-name>springSessionRepositoryFilterfilter-name>
    <url-pattern>/*url-pattern>
    <dispatcher>REQUESTdispatcher>
    <dispatcher>ERRORdispatcher>
  filter-mapping>
  <filter-mapping>
    <filter-name>springSecurityFilterChainfilter-name>
    <url-pattern>/*url-pattern>
  filter-mapping>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
  listener>

4.分布式

在其他项目中也配置security和session,未登录跳转到与之前同一个登录页面. web.xml也与第一个项目一样配置即可.applicationContext.xml大同小异.不用再配置具体的验证,程序会自己去redies获取session.下面是其他应用的applicationContext.xml.


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:s="http://www.springframework.org/schema/security"
       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.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       ">
    <s:http  entry-point-ref="restAuthenticationEntryPoint"    >
        <s:csrf disabled="true"/>
        <s:form-login authentication-success-handler-ref="mySuccessHandler" authentication-failure-handler-ref="myFailureHandler"  default-target-url="/t3.html" />
        <s:logout delete-cookies="JSESSIONID"/>
        


        <s:intercept-url pattern="/login*"   access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <s:intercept-url pattern="/secure/**"  access="permitAll"/>
        <s:intercept-url pattern="/admin/**" access="hasRole('admin')"  />

        <s:intercept-url pattern="/**" access="authenticated"/>

    s:http>

    <bean id="mySuccessHandler" class="com.xxx.security.RestAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/index.html"/>
    bean>
    <bean id="myFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>

    <s:authentication-manager>
    s:authentication-manager>



    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" />
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"      >
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}" />
        
        <property name="poolConfig" ref="poolConfig" />
    bean>
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="600">property>
    bean>

    <bean class="org.springframework.session.web.http.DefaultCookieSerializer">
        <property name="cookieName" value="JSESSIONID">property>
        <property name="cookiePath" value="/">property>
        
        <property name="domainNamePattern" value="^.+?\.(\w+\.[a-z]+)$">property>
    bean>

beans>

5.遇到的一些问题

ERR_TOO_MANY_REDIRECTS

在配置过程中,难免需要参考一些sample,当时在github上找到一个,它自己写了一个SessionServlet.把请求重定向到contextPath+"/",结果就是程序访问请求都被重定向 ‘app/’,最后就出不来了.它这个具体干嘛的,其实没搞清楚,但是还是值得记录一下.

@WebServlet("/session")
public class SessionServlet extends HttpServlet
{

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        String attributeName = req.getParameter("attributeName");
        String attributeValue = req.getParameter("attributeValue");
        req.getSession().setAttribute(attributeName, attributeValue);
        resp.sendRedirect(req.getContextPath() + "/");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        String attributeName = req.getParameter("attributeName");
        String attributeValue = req.getParameter("attributeValue");
        req.getSession().setAttribute(attributeName, attributeValue);
        resp.sendRedirect(req.getContextPath() + "/");
    }

    private static final long serialVersionUID = 2878267318695777395L;
}
 <servlet>
    <servlet-name>sessionservlet-name>
    <servlet-class>com.xx.xx.security.SessionServletservlet-class>
  servlet>

  <servlet-mapping>
    <servlet-name>sessionservlet-name>
    <url-pattern>/sessionurl-pattern>
  servlet-mapping>

spring3.1.1.release

由于有多个应用,其中部分使用的springframework版本是3.1.1.release.这里使用的spring-security也是3.1.1.release.这个版本和4.0.3.release的登录接口不一样.(目前还无法将security-3.1.1和session整合)
3.1.1的登录接口和参数名

"xxxx/j_spring_security_check" method="post"> 用户名:type="text" name="j_username" /> 密码:type="password" name="j_password" />

你可能感兴趣的:(spring-security+spring-session配置)