1. 程式人生 > >Spring Security教程(四):自定義登入頁

Spring Security教程(四):自定義登入頁

在前面的例子中,登陸頁面都是用的Spring Security自己提供的,這明顯不符合實際開發場景,同時也沒有退出和登出按鈕,因此在每次測試的時候都要通過關閉瀏覽器來登出達到清除session的效果。

一、自定義頁面

login.jsp:
<%@ 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="_spring_security_remember_me" />兩週之內不必登陸<br /> <input type="submit" value="登陸" /> <input type="reset" value="重置" /> </fieldset> </form> </body> </html>

說明:

  1. 特別要注意的是form表單的action是提交登陸資訊的地址,這是security內部定義好的,同時自定義form時,要把form的action設定為/j_spring_security_check。注意這裡要使用絕對路徑,避免登陸頁面存放的頁面可能帶來的問題。
  2. j_username,輸入登陸名的引數名稱,j_password,輸入密碼的引數名稱,這兩個正常情況下也不會修改。
  3. _spring_security_remember_me,選擇是否允許自動登入的引數名稱。可以直接把這個引數設定為一個checkbox,無需設定value,Spring Security會自行判斷它是否被選中,這也是security內部提供的,只需要配置,不需要自己實現。

 

二、配置制定的頁面

配置檔案如下:
<?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="/login.jsp" security="none"></http> -->
    <http auto-config="false">
        <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/adminPage.jsp" access="ROLE_ADMIN" />
        <intercept-url pattern="/**" access="ROLE_USER" />
        <form-login login-page="/login.jsp" default-target-url="/index.jsp"
            authentication-failure-url="/login.jsp?error=true" />
        <logout invalidate-session="true"
              logout-success-url="/login.jsp"
              logout-url="/j_spring_security_logout"/>  
    </http>
    <!-- 資料來源 -->
    <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/springsecuritydemo?useUnicode=true&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>
    
    <authentication-manager>
           <authentication-provider>
               <jdbc-user-service data-source-ref="dataSource"
                   users-by-username-query="select username,password,status as enabled from user where username = ?"
                   authorities-by-username-query="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=?"/>
           </authentication-provider>
    </authentication-manager>
</beans:beans>

說明:

  1. form-login這個標籤是配置登陸頁面的,其中的屬性login-page是配置登陸頁面的,default-target-url配置登陸成功後跳轉到的頁面,authentication-failure-url配置認證失敗後的跳轉頁面。
  2. 在上面的配置中,登陸頁面肯定是不能攔截的,任何人都應該可以訪問,<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />配置表示允許匿名使用者訪問,就是不用身份都可以訪問;還有另一種配置方式:<http pattern="/login.jsp" security="none"></http>,這種配置達到的目的都是一樣的。
  3. logout這個標籤用來配置退出或者登出,其中的屬性invalidate-session,配置否是要清除session,logout-success-url配置登出成功後的跳轉頁面,logout-url提交退出或者登出的地址,因此我們在配置退出或者登出的時候,只需要將url設定為/j_spring_security_logout即可,這個地址也是security內部實現了的。
  4. form-login標籤中還有一個特別要注意的屬性use-expressions,如果設定為true,這配置access就要做相應的改變,否則專案啟動的時候會報錯,錯誤如下:

 

如果use-expressns="true"時,則表示改為 SpEL 表示式。 SpEL 允許使用特定的訪問控制規則表示式語言。與簡單的字串如 ROLE_USER 不同,配置檔案可以指明表示式語言觸發方法呼叫、引用系統屬性、計算機值等等。http標籤中的配置改為如下:

<http auto-config="false" use-expressions="true">
        <intercept-url pattern="/login.jsp" access="permitAll" />
        <intercept-url pattern="/adminPage.jsp" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <form-login login-page="/login.jsp" default-target-url="/index.jsp"
            authentication-failure-url="/login.jsp?error=true" />
        <logout invalidate-session="true"
              logout-success-url="/login.jsp"
              logout-url="/j_spring_security_logout"/>  
    </http>
配置檔案中的其他配置在前面幾篇部落格中都有詳細的講解,這裡就不贅述了。

三、其他檔案

index.jsp

<%@ 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>Insert title here</title>
</head>
<body>
    <h1>this is a user page</h1>
    <a href="${pageContext.request.contextPath}/j_spring_security_logout">退出登陸</a>
</body>
</html>

adminPage.jsp

<%@ 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>Insert title here</title>
</head>
<body>
    <h1>this is a admin page</h1>
    <a href="${pageContext.request.contextPath}/j_spring_security_logout">退出登陸</a>
</body>
</html>

這裡定義了兩個頁面,index.jsp使用者和管理員都可以訪問,adminPage.jsp只有管理員可以訪問,同時兩個頁面都有登出按鈕,登出按鈕提交的地址也就是上面配置檔案中的地址/j_spring_security_logout。
pom.xml和前面的一樣,這裡就不貼了。

四、結果

當輸入普通使用者的使用者名稱和密碼,同時勾選2周不用登陸後,因為adminPage.jsp頁面要有管理員許可權才能訪問,所以普通使用者訪問失敗,index.jsp頁面就可以訪問;這時關閉頁面後,再次訪問資源,因為勾選了2周不用登陸,所以可以成功訪問;但是當點選退出登入後,再次訪問是就會跳轉到登陸頁面,要求登陸才能訪問。

當輸入管理員名和密碼,同時勾選2周不用登陸,驗證成功後,跳轉到index.jsp,同時adminPage.jsp也可以訪問,這時把一個頁面關閉再重新訪問資源時,因為勾選2周不用登陸,所以可以成功訪問;然後登出,這是再訪問資源時,就會跳轉到登陸頁面,要求登陸才能訪問。