登录拦截
流程:
用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。
ProvideManager 根据用户名返回对应的角色权限信息
配置方式:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailService">
<!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->
</authentication-provider>
</authentication-manager>
代码:
/**
* @author PL
* 验证配置,认证管理器,实现用户认证的入口
*/
public class MyUserDetailService implements UserDetailsService {
/**
* 登陆验证时,通过username获取用户的所有权限信息,
* 并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();
//GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");
GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_USER");
if(username.equals("admin")){
auths=new ArrayList<GrantedAuthority>();
auths.add(auth1);
// auths.add(auth2);
}
User user = new User(username, "admin", true, true, true, true, auths);
System.out.println("--------UserDetailService:"+JSONObject.toJSONString(user));
return user;
}
登录资源访问拦截
流程:
访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。
加载所有URL对应的角色:
授权管理:
完整代码:
SpringSecurity.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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 配置不过滤的资源(静态资源及登录相关) -->
<http pattern="/**/*.css" security="none"></http>
<http pattern="/**/*.jpg" security="none"></http>
<http pattern="/**/*.jpeg" security="none"></http>
<http pattern="/**/*.gif" security="none"></http>
<http pattern="/**/*.png" security="none"></http>
<http pattern="/js/*.js" security="none"></http>
<http pattern="/login.jsp" security="none"></http>
<http pattern="/getCode" security="none" /><!-- 不过滤验证码 -->
<http pattern="/test/**" security="none"></http><!-- 不过滤测试内容 -->
<http auto-config='true' >
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/main.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login.jsp" />
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<!--一个自定义的filter,必须包含 authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置 -->
<beans:bean id="myFilter"
class="cn.panlei.springsecurity.service.MyFilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailService">
<!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->
</authentication-provider>
</authentication-manager>
<!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->
<beans:bean id="myUserDetailService" class="cn.panlei.springsecurity.service.MyUserDetailService" />
<!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="myAccessDecisionManagerBean"
class="cn.panlei.springsecurity.service.MyAccessDecisionManager">
</beans:bean>
<!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->
<beans:bean id="securityMetadataSource"
class="cn.panlei.springsecurity.service.MyInvocationSecurityMetadataSource" />
</beans:beans>
MyUserDetailService.java
import java.util.ArrayList;
import java.util.Collection;
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 com.alibaba.fastjson.JSONObject;
/**
* @author PL
* 验证配置,认证管理器,实现用户认证的入口
*/
public class MyUserDetailService implements UserDetailsService {
/**
* 登陆验证时,通过username获取用户的所有权限信息,
* 并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();
//GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");
GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_USER");
if(username.equals("admin")){
auths=new ArrayList<GrantedAuthority>();
auths.add(auth1);
// auths.add(auth2);
}
User user = new User(username, "admin", true, true, true, true, auths);
System.out.println("--------UserDetailService:"+JSONObject.toJSONString(user));
return user;
}
}
MyFilterSecurityInterceptor.java
/**
*
* @author PL
*
* 资源访问是进行拦截
*/
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
// 配置文件注入
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 FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return this.securityMetadataSource;
}
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
// fi里面有一个被拦截的url
// 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
// object)这个方法获取fi对应的所有权限
// 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
this.securityMetadataSource = newSource;
}
public void destroy() {
}
public void init(FilterConfig arg0) throws ServletException {
}
}
MyAccessDecisionManager .java 表决器
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
System.out.println("进入AccessDecisionManager:"+authentication);
if (configAttributes == null) {
return;
}
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;
}
}
}
// 注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面
throw new AccessDeniedException("no right");
}
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
}
MyInvocationSecurityMetadataSource.java
/**
*
* @author PL
*
* 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问
*
*/
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
protected final Log logger = LogFactory.getLog(getClass());
//将所有的角色和url的对应关系缓存起来
//private static List<RoleUrlResource> rus = null;
private UrlMatcher urlMatcher = new AntUrlPathMatcher();
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
// tomcat启动时实例化一次
public MyInvocationSecurityMetadataSource() {
loadResourceDefine();
}
// tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系
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("/index.jsp", atts);
Collection<ConfigAttribute> attsno = new ArrayList<ConfigAttribute>();
ConfigAttribute cano = new SecurityConfig("ROLE_NO");
attsno.add(cano);
resourceMap.put("/other.jsp", attsno);
System.out.println("---------FilterInvocationSecurityMetadataSource"+JSONObject.toJSONString(resourceMap));
}
// 参数是要访问的url,返回这个url对于的所有权限(或角色)
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
/*String url = ((FilterInvocation) object).getRequestUrl();
// 查询所有的url和角色的对应关系
if (rus == null) {
rus = roleUrlDao.findAll();
}
// 匹配所有的url,并对角色去重
Set<String> roles = new HashSet<String>();
for (RoleUrlResource ru : rus) {
if (urlMatcher.pathMatchesUrl(ru.getUrlResource().getUrl(), url)) {
roles.add(ru.getRole().getRoleName());
}
}
Collection<ConfigAttribute> cas = new ArrayList<ConfigAttribute>();
for (String role : roles) {
ConfigAttribute ca = new SecurityConfig(role);
cas.add(ca);
}
return cas;*/
// 将参数转为url
//logger.info("getAttributes"+JSONObject.toJSONString(object));
// 将参数转为url
String url = ((FilterInvocation)object).getRequestUrl();
Iterator<String>ite = resourceMap.keySet().iterator();
while (ite.hasNext()) {
String resURL = ite.next();
if (urlMatcher.pathMatchesUrl(resURL, url)) {
return resourceMap.get(resURL);
}
}
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
}
UrlMatcher.java
public interface UrlMatcher {
Object compile(String paramString);
boolean pathMatchesUrl(Object paramObject, String paramString);
String getUniversalMatchPattern();
boolean requiresLowerCaseUrl();
}
获取用户输入的密码:UsernamePasswordAuthenticationFilter