這幾天項目中用到了SpringSecurity做登陸安全。所以在這寫一下也許可以幫助一下其他人,自己也熟悉一下
SpringSecurity配置文件如下:
<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">
<!-- 這個元素用來在你的應用程序中啟用基於安全的注解
<global-method-security pre-post-annotations="enabled" access-decision-manager-ref="myAccessDecisionManager"/>
-->
<http pattern="/index.html" security="none" />
<http auto-config="true">
<logout logout-success-url="/app/login" />
<form-login login-page="/app/login" default-target-url="/app/admin"
authentication-failure-url="/app/loginfailed" />
<!-- "記住我"功能,采用持久化策略(將用戶的登錄信息存放在數據庫表中) -->
<!-- <remember-me data-source-ref="dataSource" /> -->
<!-- 增加一個自定義的filter,放在FILTER_SECURITY_INTERCEPTOR之前,
實現用戶、角色、權限、資源的數據庫管理-->
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
</http>
<!-- 實現了UserDetailsService的Bean -->
<authentication-manager alias="myAuthenticationManager">
<authentication-provider user-service-ref="myUserDetailService">
<password-encoder hash="plaintext">
</password-encoder>
<!-- <password-encoder ref="passwordEncoder">
<salt-source user-property="username" />
</password-encoder> -->
</authentication-provider>
</authentication-manager>
<!--
一個自定義的filter,必須包含
authenticationManager,
accessDecisionManager,
securityMetadataSource 三個屬性。
-->
<beans:bean id="myFilter"
class="com.yihaomen.common.intercept.MyFilterSecurityInterceptor">
<!-- 用戶擁有的權限 -->
<beans:property name="authenticationManager" ref="myAuthenticationManager"></beans:property>
<!-- 用戶是否擁有所請求資源的權限 -->
<beans:property name="accessDecisionManager" ref="myAccessDecisionManager"></beans:property>
<!-- 資源與權限對應關系 -->
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"></beans:property>
</beans:bean>
</beans:beans>
所有的實現邏輯便在這個FilterInterceptor之中,主要的實現的是這個Interceptor中的三個屬性,而實現的這三個類:
Interceptor:
package com.yihaomen.common.intercept;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
// 與spring-security.xml里的myFilter的屬性securityMetadataSource對應,
// 其他的兩個組件,已經在AbstractSecurityInterceptor定義
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
public void invoke(FilterInvocation fi) throws IOException,
ServletException {
// object為FilterInvocation對象
// super.beforeInvocation(fi);源碼
// 1.獲取請求資源的權限
// 執行Collection<ConfigAttribute> attributes =
// SecurityMetadataSource.getAttributes(object);
// 2.是否擁有權限
// this.accessDecisionManager.decide(authenticated, object, attributes);
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void destroy() {
}
public void init(FilterConfig filterconfig) throws ServletException {
}
}
package com.yihaomen.comm.service;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import com.yihaomen.common.dao.ResourcesDao;
import com.yihaomen.common.domain.SysResource;
public class MyInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
@Autowired
private ResourcesDao resourcesDao;
// resourceMap及為key-url,value-Collection<ConfigAttribute>,資源權限對應Map
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public MyInvocationSecurityMetadataSourceService(ResourcesDao resourcesDao) {
this.resourcesDao = resourcesDao;
System.out.println("加載MyInvocationSecurityMetadataSourceService..."
+ resourcesDao);
loadResourceDefine();
}
// 加載所有資源與權限的關系
private void loadResourceDefine() {
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<SysResource> resources = resourcesDao.findAll();
// 加載資源對應的權限
for (SysResource resource : resources) {
Collection<ConfigAttribute> auths = resourcesDao
.loadRoleByResource(resource.getResource());
System.out.println("權限=" + auths);
resourceMap.put(resource.getResource(), auths);
}
}
}
//由資源路徑獲得權限
//object為請求的資源路徑
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
// object是一個URL,被用戶請求的url
String requestUrl = ((FilterInvocation) object).getRequestUrl();
System.out.println("requestUrl is " + requestUrl);
int firstQuestionMarkIndex = requestUrl.indexOf("?");
//如果請求的資源路徑有?后面的參數,則將?后面的切掉,以免拒絕訪問
if (firstQuestionMarkIndex != -1) {
requestUrl = requestUrl.substring(0, firstQuestionMarkIndex);
}
if (resourceMap == null) {
loadResourceDefine();
}
//
Iterator<String> ite = resourceMap.keySet().iterator();
//根據資源路徑獲得其所需的權限
while (ite.hasNext()) {
String resURL = ite.next();
if (resURL.equals(requestUrl)) {
return resourceMap.get(resURL);
}
}
return null;
}
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
}
package com.yihaomen.comm.service;
import java.sql.SQLException;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.yihaomen.common.dao.UserInfoDao;
import com.yihaomen.common.domain.SysUser;
/**
* 根據用戶名獲得其所擁有的權限,返回該用戶的狀態信息。並將其所擁有的權限放入GrantedAuthority中
* @author Administrator
*
*/
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UserInfoDao userInfoDao;
@Autowired
private UserCache userCache;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
System.out.println("username is :" + username);
SysUser user = null;
try {
user = this.userInfoDao.findByName(username);
System.out.println(user);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 獲得用戶權限
Collection<GrantedAuthority> auths = userInfoDao
.loadUserAuthorityByName(username);
boolean enables = true;
// 賬戶過期否
boolean accountNonExpired = true;
// 證書過期否
boolean credentialsNonExpired = true;
// 賬戶鎖定否
boolean accountNonLocked = true;
// 封裝成spring security的user
User userdetail = new User(username, user.getPassword(), enables,
accountNonExpired, credentialsNonExpired, accountNonLocked,
auths);
for (GrantedAuthority s : auths) {
s.getAuthority();
}
System.out.println(auths);
return userdetail;
}
public UserInfoDao getUserInfoDao() {
return userInfoDao;
}
public void setUserInfoDao(UserInfoDao userInfoDao) {
this.userInfoDao = userInfoDao;
}
//設置用戶緩存功能。
public UserCache getUserCache() {
return userCache;
}
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
}
package com.yihaomen.comm.service;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) {
return;
}
//所請求的資源擁有的權限(一個資源對多個權限)
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
//訪問所請求資源所需要的權限
String needRole = ((SecurityConfig) ca).getAttribute();
System.out.println("needRole is " + needRole);
// ga 為用戶所被賦予的權限。 needRole 為訪問相應的資源應該具有的權限。
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.trim().equals(ga.getAuthority().trim())) {
return;
}
}
}
//沒有權限
throw new AccessDeniedException("沒有權限訪問!");
}
public boolean supports(ConfigAttribute arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}
