[Spring ---- Security] seven, RememberMe configuration

I. Overview

RememberMe means that the user can remember the logged on user's credentials between Session on the site, it is popular again after successful authentication user login can no longer enter a user name and password to log in automatically within a certain time to develop. This process sends a cookie to the client browser saved by the server, the next time the browser and then access the server the server can automatically detect the client's cookie, triggering an automatic registration operation according to the cookie value.

 

Remember-Me Spring Security function is usually implemented in two ways.

  • One is simple to use encryption to ensure the security of cookie-based token
  • Another is to save the token generated by a database or other persistent storage mechanism

The difference between the two approaches:

  • The first in an unsafe manner, that is to get the user to remember after I realize the function token, any user can be automatically logged by the token before the token expires. If you find yourself token stolen, then he can change his password immediately to remember it all my token failure
  • If you want our applications to a more secure, then use the second approach. The second way is to explain in detail.

Fundamental

 

 

Second, based on a simple encryption method

First need to change login.jsp, I remember the checkbox to modify the name of a custom name

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>自定义登陆页面</title>
    </head>
    <body>
        <div class="error ${param.error == true ? '' : 'hide'}">  
                登陆失败<br>  
            ${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}  
        </div>  
        <form method="post" action="${pageContext.request.contextPath}/j_spring_security_check" style="width:260px; text-align: center">  
            <fieldset>  
                <legend>登陆</legend>  
                用户: <input type="text" name="j_username" style="width: 150px;" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" />
                <br/>  
                   密码: <input type="password" name="j_password" style="width: 150px;" />
                  <br/>  
                <input type="checkbox" name="remember-me" />记住我<br/>  
                <input type="submit" value="登陆" />
                <input type="reset" value="重置" />
            </fieldset>  
       </form>  
    </body>
</html>

Of particular note is that these two approaches should provide a UserDetailsService at configuration time, this thing is actually an implementation class jdbc-user-service label previously configured, the configuration code is as follows:

<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
  <beans:property name="usersByUsernameQuery" value="select username,password,status as enabled from user where username = ?" />
  <beans:property name="authoritiesByUsernameQuery" value="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and user_role.role_id=role.id and user.username=?" />
  <beans:property name="dataSource" ref="dataSource" />
</beans:bean>

Description:

  • dataSource is connected to a data source database;
  • usersByUsernameQuery is to configure jdbc-user-service time users-by-username-query, this is the sql statement to query the user based on the user name;
  • Similarly authoritiesByUsernameQuery is the corresponding authorities-by-username-query, the query for permissions based on user names corresponding.

Here configured rememberMe filter:

  <!-- Remember-Me 对应的 Filter -->
    <beans:bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
        <beans:property name="rememberMeServices" ref="rememberMeServices" />
        <beans:property name="authenticationManager" ref="authenticationManager" />
    </beans:bean>

This filter is configured such that not only function, but also to add it to the Security of FilterChain go, with <custom-filter ref = "rememberMeFilter " position = "REMEMBER_ME_FILTER" /> to, this filter to further a rememberMeServices the authenticationManager and a user authentication, this latter fact authentication-manager configured things, the need to configure the front, arranged as follows:

   <!-- RememberMeServices 的实现 -->
    <beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
        <beans:property name="userDetailsService" ref="userDetailsService" />
        <beans:property name="key" value="sunny" />
        <!-- 指定 request 中包含的用户是否选择了记住我的参数名 -->
        <beans:property name="parameter" value="remember-me" />
        <beans:property name="tokenRepository">
            <beans:bean class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
                <beans:property name="dataSource" ref="dataSource"/>
                <!-- 是否在启动时创建持久化 token 的数据库表 若为true,但数据有这个表时,会启动失败,提示表已存在 -->
                <beans:property name="createTableOnStartup" value="false"/>
             </beans:bean>
        </beans:property>
    </beans:bean>
  • This configuration userDetailsService is above configuration, a direct reference to the above;
  • key is a token in the key , this key can be used manner token to be modified, and further back to the key configuration rememberMeAuthenticationProvider the key to be the same;
  • parameter is the login screen click remember password checkbox of name value, this must be consistent, otherwise no effect.
  • Also outside the initial configuration of a user do to remember the password authentication authenticationManager
  <!-- 记住密码 ,key 值需与对应的 RememberMeServices 保持一致 -->
    <beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
        <beans:property name="key" value="sunny" />
    </beans:bean>

But also add it to the authentication-manager tab to

   <!--认证管理-->
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="userDetailsService"></authentication-provider>
        <!-- 记住密码 -->
        <authentication-provider ref="rememberMeAuthenticationProvider"></authentication-provider>
    </authentication-manager>

The final surface will rememberMeServices added to myUsernamePasswordAuthenticationFilter go.

  <!-- 自定义登录过滤器 -->
    <beans:bean id="myUsernamePasswordAuthenticationFilter" class="com.sunny.auth.MyUsernamePasswordAuthenticationFilter">
        <beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler" />
        <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" />
        <!-- 记住我 -->
        <beans:property name="rememberMeServices" ref="rememberMeServices" />
    </beans:bean>

 

The final spring-security.xml profile as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security.xsd">
    <!-- 不需要访问权限 -->
    <http pattern="/page/login.jsp" security="none"></http>
    <http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
        <!-- <form-login login-page="/page/login.jsp" default-target-url="/page/admin.jsp" authentication-failure-url="/page/login.jsp?error=true" /> -->
        <logout invalidate-session="true" logout-success-url="/page/login.jsp" logout-url="/j_spring_security_logout" />
        <!-- 自定义登录过滤器 -->
        <custom-filter ref="myUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER" />
         <!--替换默认REMEMBER_ME_FILTER-->    
        <custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
        <!-- 通过配置custom-filter来增加过滤器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。 -->
        <custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
    </http>
    <!-- 登录切入点 -->
    <beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:property name="loginFormUrl" value="/page/login.jsp"/>
    </beans:bean>
    <!-- 自定义登录过滤器 -->
    <beans:bean id="myUsernamePasswordAuthenticationFilter" class="com.sunny.auth.MyUsernamePasswordAuthenticationFilter">
        <beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler" />
        <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" />
        <!-- 记住我 -->
        <beans:property name="rememberMeServices" ref="rememberMeServices" />
    </beans:bean>
    <!-- 登录成功 -->
    <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <beans:property name="defaultTargetUrl" value="/page/admin.jsp" />
    </beans:bean>
     <!-- 登录失败 -->
    <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <beans:property name="defaultFailureUrl" value="/page/login.jsp" />
    </beans:bean> 
    
    <!-- 认证过滤器 -->
    <beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <!-- 用户拥有的权限 -->
        <beans:property name="accessDecisionManager" ref="accessDecisionManager" />
        <!-- 用户是否拥有所请求资源的权限 -->
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <!-- 资源与权限对应关系 -->
        <beans:property name="securityMetadataSource" ref="securityMetadataSource" />
    </beans:bean>
    
    <!-- 授权管理器 -->
    <beans:bean id="accessDecisionManager" class="com.sunny.auth.MyAccessDecisionManager">
    </beans:bean>
    
     <!--自定义的切入点-->
    <beans:bean id="securityMetadataSource" class="com.sunny.auth.MyFilterInvocationSecurityMetadataSource">
        <beans:property name="builder" ref="builder"/>
    </beans:bean>
    
    <beans:bean id="builder" class="com.sunny.auth.JdbcRequestMapBulider"> 
        <beans:property name="dataSource" ref="dataSource" /> 
        <beans:property name="resourceQuery" value="select re.res_string,r.name from role r,resc re,resc_role rr where r.id=rr.role_id and re.id=rr.resc_id" /> 
    </beans:bean>
    
     <!--认证管理-->
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="userDetailsService"></authentication-provider>
        <!-- 记住密码 -->
        <authentication-provider ref="rememberMeAuthenticationProvider"></authentication-provider>
    </authentication-manager>
    
    
    <!-- Remember-Me 对应的 Filter -->
    <beans:bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
        <beans:property name="rememberMeServices" ref="rememberMeServices" />
        <beans:property name="authenticationManager" ref="authenticationManager" />
    </beans:bean>
    <!-- RememberMeServices 的实现 -->
    <beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
        <beans:property name="userDetailsService" ref="userDetailsService" />
        <beans:property name="key" value="sunny" />
        <!-- 指定 request 中包含的用户是否选择了记住我的参数名 -->
        <beans:property name="parameter" value="remember-me" />
        <beans:property name="tokenRepository">
            <beans:bean class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
                <beans:property name="dataSource" ref="dataSource"/>
                <!-- 是否在启动时创建持久化 token 的数据库表 若为true,但数据有这个表时,会启动失败,提示表已存在 -->
                <beans:property name="createTableOnStartup" value="false"/>
             </beans:bean>
        </beans:property>
    </beans:bean>
    
    <!-- 记住密码 ,key 值需与对应的 RememberMeServices 保持一致 -->
    <beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
        <beans:property name="key" value="sunny" />
    </beans:bean>
    
    <beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
        <beans:property name="usersByUsernameQuery" value="select username,password,status as enabled from user where username = ?" />
        <beans:property name="authoritiesByUsernameQuery" value="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and user_role.role_id=role.id and user.username=?" />
        <beans:property name="dataSource" ref="dataSource" />
    </beans:bean>
    
</beans:beans>

spring-dataSource.xml remain unchanged

<?xml version="1.0" encoding="UTF-8"?>
<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/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security.xsd">
    <!-- 数据源 -->
    <beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <!-- 此为c3p0在spring中直接配置datasource c3p0是一个开源的JDBC连接池 -->
        <beans:property name="driverClass" value="com.mysql.jdbc.Driver" />
 
        <beans:property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&amp;characterEncoding=UTF-8" />
        <beans:property name="user" value="root" />
        <beans:property name="password" value="" />
        <beans:property name="maxPoolSize" value="50"></beans:property>
        <beans:property name="minPoolSize" value="10"></beans:property>
        <beans:property name="initialPoolSize" value="10"></beans:property>
        <beans:property name="maxIdleTime" value="25000"></beans:property>
        <beans:property name="acquireIncrement" value="1"></beans:property>
        <beans:property name="acquireRetryAttempts" value="30"></beans:property>
        <beans:property name="acquireRetryDelay" value="1000"></beans:property>
        <beans:property name="testConnectionOnCheckin" value="true"></beans:property>
        <beans:property name="idleConnectionTestPeriod" value="18000"></beans:property>
        <beans:property name="checkoutTimeout" value="5000"></beans:property>
        <beans:property name="automaticTestTable" value="t_c3p0"></beans:property>
    </beans:bean>
</beans:beans>

 

Third, based on the persistence of the way configuration

The first of the above rememberService implementation class TokenBasedRememberMeServices simple explanation before these configuration, the class is mainly based on a simple class that implements the encryption token.

  • After TokenBasedRememberMeServices will select the Remember me successfully logged in user, generates a token that contains information about the cookie sent to the client;
  • If the user fails to log cookie is deleted achieve Remember-Me saved the client.
  • Log in automatically when needed, it will determine whether the cookie contains information regarding Remember-Me consistent with the system, consistent returns for a RememberMeAuthenticationToken RememberMeAuthenticationProvider process, inconsistencies will be removed Remember-Me cookie client.
  • TokenBasedRememberMeServices also implements the interface LogoutHandler Spring Security, so it can clear the Remember-Me cookie as soon as the user logs out.

The essence of the base configuration persistent way is different class, used to achieve class configuration-based persistent way: PersistentTokenBasedRememberMeServices, to see the name to know their role, is the token for persistence saved, to save the data corresponding to that save for the development of the place, this place is saved to the specified use PersistentTokenRepository, Spring Security to achieve this, there are two, InMemoryTokenRepositoryImpl and JdbcTokenRepositoryImpl. The former token is stored in memory, typically used for testing, which is a token stored in the database. PersistentTokenBasedRememberMeServices default is that the former, we can specify PersistentTokenRepository used by its tokenRepository property. This example uses JdbcTokenRepositoryImpl be persistently saved, apparently to save data to the database, be sure to have a table that security is also provided and is fixed, sql statement is:

create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)

This can create a table in the database.

After the table is created as follows:

 

Fourth, the effect of two configurations

I did not check [Remember] when landing after exit time and then access /page/admin.jsp would require re-landing. At the same time the database will not add data.

When the check [Remember Me] after the landing, even after logout, then visit /page/admin.jsp can not directly access, at the same time, the database will be saved login information, comparing two data can also be found, in addition to user name has not changed, because other data will be updated second visit

 

Guess you like

Origin blog.csdn.net/ningjiebing/article/details/89411066