沒事就來了解下spring security.網上找了很多資料。有過時的,也有不是很全面的。各種問題也算是讓我碰了個遍。這樣吧。我先把整個流程寫下來,之后在各個易混點分析吧。
1.建立幾個必要的頁面。
login.jsp index.jsp user.jsp admin.jsp error.jsp 后面幾個只是用來做跳轉的,里面沒什么要注意的,就貼出login里面的吧
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>login</title> </head> <body> <form action="/mavenTest/j_spring_security_check" method="post"> 賬戶:<input type="text" name="j_username" id="username"/> 密碼:<input type="text" name="j_password" id="password"/> <input type="submit" value="登陸"/> </form> </body> </html>
2.配置web.xml 沒有用的就沒有粘貼了
<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>
3.忘記吧pom.xml文件給出來了 版本為<org.springframework-version>3.2.3.RELEASE</org.springframework-version>
<!-- spring security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${org.springframework-version}</version> </dependency>
4.配置spring security .xml文件
<?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.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!-- 不需要進行安全認證的資源 --> <http pattern="/resources/**" security="none" /> <http pattern="/login.jsp" security="none"/> <!-- 資源所需要的權限 --> <http auto-config='true'> <form-login login-page="/login.jsp" default-target-url="/index.jsp" authentication-failure-url="/login.jsp?error=true" /> <logout logout-success-url="/index.jsp"/> <!-- 嘗試訪問沒有權限的頁面時跳轉的頁面 --> <access-denied-handler error-page="/error-noauth.jsp"/> <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> </http> <!-- 自定義一個filter,必須包含authenticationManager,accessDecisionManager, securityMetadataSource三個屬性 所有的功能都在這三個類中實現--> <beans:bean id="myFilter" class="com.demo.im.model.security.MyFilterSecurotyInterceptor"> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="accessDecisionManager" ref ="myAccessDecisionManagerBean"/> <beans:property name="securityMetadataSource" ref="securityMetadaSource"/> </beans:bean> <!-- 認證管理器,實現用戶認證的入口,主要實現userdetailsservice --> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="myUserDetailService"> </authentication-provider> </authentication-manager> <beans:bean id="myUserDetailService" class="com.demo.im.model.security.DefaultUserDetailsService"> </beans:bean> <!-- 訪問決策,決定某個用戶具有的角色,是否有足夠的權限去訪問某個資源 --> <beans:bean id="myAccessDecisionManagerBean" class="com.demo.im.model.security.MyAccessDecisionManager" /> <beans:bean id="securityMetadaSource" class="com.demo.im.model.security.MyInvocationSecurityMetadaSource"/> </beans:beans>
5.MyFilterSecurotyInterceptor 自定義filter的代碼如下。
其中最核心的代碼是 invoke() 方法中的
InterceptorStatusToken token = super.beforeInvocation(fi);
這一句在dofilter之前進行權限驗證,而具體的實現交給了accessDecisionManager,下面將繼續討論
package com.demo.im.model.security; 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.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 MyFilterSecurotyInterceptor extends AbstractSecurityInterceptor implements Filter { private FilterInvocationSecurityMetadataSource securityMetadataSource; @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response,chain); invoke(fi); } @Override public void destroy() { // TODO Auto-generated method stub } @Override public Class<?> getSecureObjectClass() { // TODO Auto-generated method stub return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { // TODO Auto-generated method stub return this.securityMetadataSource; } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return securityMetadataSource; } public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) { this.securityMetadataSource = securityMetadataSource; } public void invoke(FilterInvocation fi) throws IOException ,ServletException{ InterceptorStatusToken token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } catch (Exception e) { // TODO: handle exception }finally{ super.afterInvocation(token, null); } } }
6.DefaultUserDetailsService的實現
在這個類中你可以通過讀取數據庫來進行權限賦值,下面的代碼未注解的是我寫的靜態的
package com.demo.im.model.security; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import com.demo.im.entity.Role; import com.demo.im.entity.TUser; import com.demo.im.model.dao.TUserMapper; @Service public class DefaultUserDetailsService implements UserDetailsService { @Autowired TUserMapper userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub System.out.println("userDetail********"); Collection <GrantedAuthority> auths = new ArrayList<GrantedAuthority>(); GrantedAuthority auth2 = new GrantedAuthorityImpl("ROLE_ADMIN"); auths.add(auth2); User user = new User(username,"1111",true,true,true,true,auths); return user; // TUser paramU = new TUser(); // paramU.setUsername(username); // List<TUser> userList = userDao.selectByUserParam(paramU); // TUser user = new TUser(); // if(userList==null || userList.size()<=0){ // throw new UsernameNotFoundException("username"); // } // //得到用戶權限 // if(user.getRoles()!=null && user.getRoles().size()>0){ // // for(Role role : user.getRoles()){ // GrantedAuthority grantedAuthority = new GrantedAuthorityImpl( // role.getRolecode().toUpperCase()); // auths.add(grantedAuthority); // } // // } // user.setAuthorities(auths); // return user; } }
7.MyInvocationSecurityMetadaSource類的內容
這個類說白了就是從數據庫中取出資源對應的權限,我這里也是寫的靜態的,修改為動態的方法也很簡單,執行一個查詢就行了
package com.demo.im.model.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; public class MyInvocationSecurityMetadaSource implements FilterInvocationSecurityMetadataSource{ // private UrlMatcher urlMatcher = new AntUrlPathMatcher(); private static Map<String,Collection<ConfigAttribute>> resourceMap = null; public MyInvocationSecurityMetadaSource(){ loadResourceDefine(); } private void loadResourceDefine(){ resourceMap = new HashMap<String, Collection<ConfigAttribute>>(); Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); ConfigAttribute ca = new SecurityConfig("ROLE_ADMIN"); atts.add(ca); resourceMap.put("/user.jsp", atts); resourceMap.put("/admin.jsp", atts); resourceMap.put("/index.jsp",atts); } @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { // TODO Auto-generated method stub String url = ((FilterInvocation)object).getRequestUrl(); Iterator<String> ite = resourceMap.keySet().iterator(); while(ite.hasNext()){ String resURL = ite.next(); if(url.equalsIgnoreCase(resURL)){ return resourceMap.get(resURL); } } return null; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { // TODO Auto-generated method stub return null; } @Override public boolean supports(Class<?> clazz) { // TODO Auto-generated method stub return true; } }
8.最后就是決策類了MyAccessDecisionManager
這個類就更好理解了,我們知道了用戶的角色,我們也知道資源的角色,對比一下,也就是decide()一致就返回,不行就拋異常
package com.demo.im.model.security; 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{ @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes == null){ return ; } System.out.println(object.toString()); Iterator<ConfigAttribute> ite = configAttributes.iterator(); while(ite.hasNext()){ ConfigAttribute ca = ite.next(); String needRole = ((SecurityConfig)ca).getAttribute(); for(GrantedAuthority ga : authentication.getAuthorities()){ if(needRole.equals(ga.getAuthority())){ return ; } } } throw new AccessDeniedException("no right"); } @Override public boolean supports(ConfigAttribute arg0) { // TODO Auto-generated method stub return true; } @Override public boolean supports(Class<?> arg0) { // TODO Auto-generated method stub return true; } }
9總結:
到這里一個基本的spring security算是搭建完了。過會在來說說我遇到的坑。方便以后大家找自己的坑。
