不知道我的web.xml 大家都理解了没。 废话确实有点多,可能很多知识点,大家都知道,可是我学的时候,压根什么都不懂啊....
这篇我们要讲刽子手 securityConfig。 为什么要说他是刽子手呢? 因为他是无良掌柜的小工,直接的操盘手......
<?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/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <http access-denied-page = "/accessDenied.jsp"> <!-- 访问拒绝页面 --> <form-login login-page="/login.jsp"/> <!-- 定义登陆界面 --> <intercept-url pattern="/login.jsp" filters="none"/> <session-management> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/> <!-- 用户最大登录数设置为1 ,超过则引发异常 --> </session-management> <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> <!-- 自定义FILTER ,FilterSecurityInterceptor 负责授权--> </http> <!-- myFilter --> <beans:bean id = "myFilter" class = "com.qbt.spring.security.MyFilterSecurityInterceptor"> <beans:property name="authenticationManager" ref ="authenticationManager"></beans:property> <!-- 登陆验证 ,验证你的用户名密码噼里啪啦--> <beans:property name="securityMetadataSource" ref = "securityMetadataSource"></beans:property> <!-- 资源数据源的定义 ,神马权限对应神马资源 噼里啪啦--> <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean"></beans:property> <!-- 访问决策 有没有权限访问资源 噼里啪啦--> </beans:bean> <!-- 验证配置,认证管理器,实现UserDetailService接口 --> <!-- authenticationManager 可以有多个provider提供信息,我们用myUserDetailService获取信息 --> <!-- Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现, 但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。 检查后或者返回Authentication对象或者抛出异常 --> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="myUserDetailService"></authentication-provider> </authentication-manager> <!-- 获取user数据,可以从数据库中获取用户密码,角色等! --> <beans:bean id = "myUserDetailService" class = "com.qbt.spring.security.MyUserDetailService"></beans:bean> <!-- 访问决策器,决定用户的角色,访问的权限 --> <beans:bean id = "myAccessDecisionManagerBean" class = "com.qbt.spring.security.MyAccessDecisionManager"></beans:bean> <!-- 资源数据源的定义 什么资源对应什么权限,或者什么资源能被什么角色访问--> <beans:bean id = "securityMetadataSource" class = "com.qbt.spring.security.MyInvocationSecurityMetadataSource"></beans:bean> </beans:beans>
可以看出来,这里的核心过滤器是我们自己实现的MyFilterSecurityInterceptor
//下面,贴出他的代码
package com.qbt.spring.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; /** *登陆后,每次访问资源都会访问这个拦截器 ,执行doFilter, 调用invoke, *super.beforeInvocation() 会调用SecurityMetadataSource 的getAttributes方法获取fi对应的所有权限,再调用decide的方法,判断是否有权限 *然后调用下一个拦截器。 * */ public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{ //配置文件注入 private FilterInvocationSecurityMetadataSource securityMetadataSource; //登陆后,每次访问资源都通过这个拦截器拦截 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } private void invoke(FilterInvocation fi) throws IOException, ServletException { //fi 封装了request,response,chain //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限 //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够 InterceptorStatusToken token = super.beforeInvocation(fi); //这里的token里面到底是什么呢? 八一八 来! /** * 源码--- * protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); //预言 //省略 Collection attributes = obtainSecurityMetadataSource().getAttributes(object); //调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限 if(attributes == null) { //抛异常省略 return null; } //省略 //判断是否需要对认证实体重新认证,默认为否 Authentication authenticated = authenticateIfRequired(); try {//decide方法来校验用户的权限是否足够 accessDecisionManager.decide(authenticated, object, attributes); } catch(AccessDeniedException accessDeniedException) {//抛异常 publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } //省略 return new InterceptorStatusToken(authenticated, true, attributes, object); } * */ try{ fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally{ super.afterInvocation(token, null); } } @Override public void init(FilterConfig arg0) throws ServletException { } //相对应的MyAccessDecisionManager的supports方法必须放回true,否则会提醒类型错误 @Override public Class<? extends Object> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource ){ this.securityMetadataSource = newSource; } @Override public void destroy() { } }
//累啊...
然后我们来看看 authenticationManager
package com.qbt.spring.security; import java.util.ArrayList; import java.util.Collection; import org.springframework.dao.DataAccessException; 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; public class MyUserDetailService implements UserDetailsService{ /** * 获取用户信息,返回User放到Spring的全局缓存SecurityContentHolder中,让其他过滤器使用 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { UserDetails user = null; Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>(); //定义一个权限集合 GrantedAuthorityImpl auth1 = new GrantedAuthorityImpl("ROLE_ADMIN"); //定义一个管理员权限 GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl("ROLE_USER"); //定义一个用户权限 System.out.println("**********MyUserDetailService登陆验证,通过用户名获取权限************"); if(username.equals("admin")){ auths.add(auth2); auths.add(auth1); }else{ auths.add(auth2); } // username, password, enable, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities user = new User(username, "admin", true, true, true, true, auths); return user; } }
然后再看看MyInvocationSecurityMetadataSource
package com.qbt.spring.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; import org.springframework.security.web.util.AntUrlPathMatcher; import org.springframework.security.web.util.UrlMatcher; /** * @author ORC * FilterInvocationSecurityMetadataSource 继承于 SecurityMetadataSource * */ public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private UrlMatcher urlMatcher = new AntUrlPathMatcher(); public static Map<String,Collection<ConfigAttribute>> resourceMap = null; public MyInvocationSecurityMetadataSource () { loadResourceDefine(); } /** * 获取角色和资源的相对应的关系,在tomcat启动时加载。 * 因为是在tomcat启动时就要加载,所以如果权限对应关系改变的话,就需要重新获取 * 如果要调用Dao的话,但是在启动的时候,这个Dao可能还没有加载 * 所以如果要在这里调用数据库的话,要自己写sessionfaction,sql/hql * 还有一种方法就是,在getAttributes方法里调用Dao,因为这个方法是在tomcat启动之后才调用的 */ private void loadResourceDefine() { resourceMap = new HashMap<String,Collection<ConfigAttribute>>(); Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); ConfigAttribute ca = new SecurityConfig("ROLE_USER"); atts.add(ca); resourceMap.put("/index.jsp", atts); Collection<ConfigAttribute> attsNo = new ArrayList<ConfigAttribute>(); ConfigAttribute no = new SecurityConfig("ROLE_NO"); attsNo.add(no); resourceMap.put("/other.jsp", attsNo); System.out.println("*************MyInvocationSecurityMetadataSource 调用,获取角色和资源对应值*******************"); } @Override public Collection<ConfigAttribute> getAttributes(Object obj) throws IllegalArgumentException { String url = ((FilterInvocation)obj).getRequestUrl(); System.out.println("*************MyInvocationSecurityMetadataSource getAttribute调用,获取资源所对应的角色集合*******************"); Iterator<String>ite = resourceMap.keySet().iterator(); while(ite.hasNext()){ String resUrl = ite.next(); if(urlMatcher.pathMatchesUrl(resUrl, url)) { return resourceMap.get(resUrl); } } return null; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> class1) { return true; } }
再看MyAccessDecisionManager
package com.qbt.spring.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 obj, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes == null) { return; } System.out.println("*************MyAccessDecesionManager 判断用户是否有权限**************"); Iterator<ConfigAttribute> itr = configAttributes.iterator(); while(itr.hasNext()){ ConfigAttribute ca = itr.next(); String needRole = ((SecurityConfig)ca).getAttribute(); for(GrantedAuthority ga : authentication.getAuthorities()){ if(needRole.equals(ga.getAuthority())){ return; } } } throw new AccessDeniedException("No Right"); } //这个 supports(ConfigAttribute) 方法在启动的时候被 //AbstractSecurityInterceptor调用,来决定AccessDecisionManager //是否可以执行传递ConfigAttribute @Override public boolean supports(ConfigAttribute configattribute) { return false; } //supports(Class)方法被安全拦截器实现调用, //包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。 @Override public boolean supports(Class<?> class1) { return true; } }
时间: 2024-11-12 20:55:51