在使用Spring Security配置Web應用之前,首先要準備一個基於Maven的Spring框架創建的Web應用(Spring MVC不是必須的),本文的內容都是基於這個前提下的。
pom.xml添加依賴
除了Spring框架本身的一些依賴包,還需要在pom.xml中添加Spring Security的依賴包:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
入門環境配置
要想使用Spring Security,首先需要在web.xml配置一個過濾器,注意過濾器的filter-name必須是"springSecurityFilterChain":
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
另外還需要配置Spring Security配置文件,並將這個文件添加到Spring Application Context中:
<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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http use-expressions="false">
<!-- 訪問所有頁面都需要有USER權限 -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- 登錄功能 -->
<form-login />
<!-- 登出功能 -->
<logout />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<!-- 這裏創建兩個用戶,可以通過用戶名密碼登錄 -->
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
只需完成以上兩個配置,啓動服務器,用瀏覽器打開這個Web應用的任意一個頁面,都會跳轉到一個登錄頁,這個登錄頁面時Spring Security自動生成的。
在登錄頁面輸入錯誤的用戶名密碼,就會登錄失敗並有提示。輸入正確的用戶名密碼,則登錄成功,就可以進入Web應用的頁面。一個最簡單的基於Spring Security的Web應用已經完成!
指定登錄頁面
由於Spring Security默認的登錄頁面非常簡陋,一般不會直接使用,通常會指定一個自定義的登錄頁面:
<http use-expressions="false">
<!-- 登錄頁面不需要控制權限 -->
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<!-- 訪問其他所有頁面都需要有USER權限 -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- 配置登錄頁面地址login-page、登錄失敗後的跳轉地址authentication-failure-url -->
<form-login login-page='/login.jsp' authentication-failure-url='/login.jsp?error' />
<!-- 登出功能 -->
<logout />
</http>
自定義的登錄頁面jsp中的登錄表單:
<c:url value="/login" var="loginUrl" />
<form action="${loginUrl}" method="post">
<c:if test="${param.error != null}">
<p>Invalid username and password.</p>
</c:if>
<c:if test="${param.logout != null}">
<p>You have been logged out.</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username" />
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password" />
</p>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn">Log in</button>
</form>
登錄表單提交的頁面地址是/login,method是POST請求。爲了安全,防止惡意的CSRF攻擊,Spring Security需要校驗form表單中的hidden域提交的內容。
登出
<c:url value="/logout" var="logoutUrl" />
<form action="${logoutUrl}" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<input type="submit" value="退出" />
</form>
登出請求地址/logout,method是POST請求。
登錄用戶信息從數據庫獲取
<authentication-manager>
<authentication-provider user-service-ref='userDetailsService' />
</authentication-manager>
<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="usersByUsernameQuery" value="select username, password, true from t_user where username = ?" />
<beans:property name="authoritiesByUsernameQuery" value="select username, role from t_user_role where username = ?" />
</beans:bean>
以上配置還可以簡化爲:<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, true from t_user where username = ?"
authorities-by-username-query="select username, role from t_user_role where username = ?" />
</authentication-provider>
</authentication-manager>
登錄用戶信息通過其他方式獲取
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 以下可以替換成用其他方式獲取用戶信息
if(username.equals("xxg")) {
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
auths.add(authority);
User user = new User(username, "123456", auths);
return user;
} else {
throw new UsernameNotFoundException("用戶不存在");
}
}
}
將該實現類配置在Spring Security配置文件中:
<authentication-manager>
<authentication-provider user-service-ref='userDetailsService' />
</authentication-manager>
<beans:bean id="userDetailsService" class="com.xxg.service.UserDetailsServiceImpl" />
配置不受Spring Security管理的URL
<http pattern="/resources/css/**" security="none"/>
<http pattern="/resources/images/**" security="none"/>
<http pattern="/resources/js/**" security="none"/>
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<logout />
</http>
獲取登錄用戶信息
獲取用戶名:
httpServletRequest.getRemoteUser(); // Servlet標準,推薦使用
SecurityContextHolder.getContext().getAuthentication().getName();
獲取用戶ROLE:
SecurityContextHolder.getContext().getAuthentication().getAuthorities();
判斷用戶是否擁有ROLE:
httpServletRequest.isUserInRole("ADMIN");