近期对Spring+Security的框架进行了一些简单的研究,将研究的结果贴出来,供大家学习参考。
废话少说,还是代码来的直接明了。
首先需要一些相关的源代码:
MyAccessDecisionManager.java:
/** * */ package com.simonsw.security; import java.util.Collection; import java.util.Iterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; /** * @author Simon Lv * @since Oct 31, 2013 */ public class MyAccessDecisionManager implements AccessDecisionManager { private Logger logger = LoggerFactory.getLogger(getClass()); /* * (non-Javadoc) * * @see * org.springframework.security.access.AccessDecisionManager#decide(org. * springframework.security.core.Authentication, java.lang.Object, * java.util.Collection) */ @Override public void decide(Authentication authentication, Object obj, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes == null) { return; } // 所请求的资源拥有的权限(一个资源对多个权限) Iterator<ConfigAttribute> iterator = configAttributes.iterator(); while (iterator.hasNext()) { ConfigAttribute configAttribute = iterator.next(); // 访问所请求资源所需要的权限 String needPermission = configAttribute.getAttribute(); logger.debug("[MyAccessDecisionManager] needPermission is " + needPermission); // 用户所拥有的权限authentication for (GrantedAuthority ga : authentication.getAuthorities()) { logger.debug("[MyAccessDecisionManager] ga.getAuthority() is " + ga.getAuthority()); if (needPermission.contains((ga.getAuthority()))) { return; } } } // 没有权限让我们去捕捉 throw new AccessDeniedException(" 没有权限访问!"); } /* * (non-Javadoc) * * @see * org.springframework.security.access.AccessDecisionManager#supports(org * .springframework.security.access.ConfigAttribute) */ @Override public boolean supports(ConfigAttribute arg0) { return true; } /* * (non-Javadoc) * * @see * org.springframework.security.access.AccessDecisionManager#supports(java * .lang.Class) */ @Override public boolean supports(Class<?> arg0) { return true; } }
MyAuthenticationManager.java:
/** * */ package com.simonsw.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.simonsw.base.entity.Resource; import com.simonsw.base.entity.Role; import com.simonsw.base.entity.RoleResource; import com.simonsw.base.entity.UserRole; import com.simonsw.base.entity.Users; import com.simonsw.base.service.RoleResourceService; import com.simonsw.base.service.UserRoleService; import com.simonsw.base.service.UserService; import com.simonsw.common.util.StringUtils; /** * @author Simon Lv * @since Oct 31, 2013 */ public class MyAuthenticationManager implements UserDetailsService { @Autowired private UserService userService; @Autowired private UserRoleService userRoleService; @Autowired private RoleResourceService roleResourceService; private Logger logger = LoggerFactory.getLogger(getClass()); /* * (non-Javadoc) * * @see org.springframework.security.core.userdetails.UserDetailsService# * loadUserByUsername(java.lang.String) */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.debug("[MyAuthenticationManager] username ==> " + username); if (!StringUtils.isEmpty(username)) { throw new UsernameNotFoundException("用户名不能为空!"); } Users user = userService.getUserByName(username); Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(user); boolean enables = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; // 封装成spring security的user User userdetail = new User(user.getUsername(), user.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths); return userdetail; } // 取得用户的权限 private Set<GrantedAuthority> obtionGrantedAuthorities(Users user) { Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>(); List<Resource> resources = new ArrayList<Resource>(); Role role; List<RoleResource> roleResources; List<UserRole> userRoles = userRoleService.getUserRoleByUserId(user); for (UserRole userRole : userRoles) { role = userRole.getRole(); roleResources = roleResourceService.getUserRoleByRoleId(role); for (RoleResource roleResource : roleResources) { resources.add(roleResource.getResource()); } } for (Resource res : resources) { authSet.add(new SimpleGrantedAuthority(res.getModelname())); } return authSet; } }
MyLogoutSuccessHandler.java:
/** * */ package com.simonsw.security; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; /** * @author Simon Lv * @since Nov 5, 2013 */ public class MyLogoutSuccessHandler implements LogoutSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /* * (non-Javadoc) * * @see * org.springframework.security.web.authentication.logout.LogoutSuccessHandler * #onLogoutSuccess(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse, * org.springframework.security.core.Authentication) */ @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if (authentication != null) { logger.debug(authentication.getName() + "Logout==>"); } response.sendRedirect(request.getContextPath()); } }
MySecurityFilter.java:
/** * */ package com.simonsw.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.slf4j.Logger; import org.slf4j.LoggerFactory; 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; /** * @author Simon Lv * @since Oct 31, 2013 */ public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter { private Logger logger = LoggerFactory.getLogger(getClass()); // 与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应, // 其他的两个组件,已经在AbstractSecurityInterceptor定义 private FilterInvocationSecurityMetadataSource securityMetadataSource; /* * (non-Javadoc) * * @see * org.springframework.security.access.intercept.AbstractSecurityInterceptor * #obtainSecurityMetadataSource() */ @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } /* * (non-Javadoc) * * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, * javax.servlet.ServletResponse, javax.servlet.FilterChain) */ @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 { logger.debug("[MySecurityFilter] 用户发送请求!"); InterceptorStatusToken token = null; token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return securityMetadataSource; } public void setSecurityMetadataSource( FilterInvocationSecurityMetadataSource securityMetadataSource) { this.securityMetadataSource = securityMetadataSource; } /* * (non-Javadoc) * * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ @Override public void init(FilterConfig arg0) throws ServletException { } /* * (non-Javadoc) * * @see javax.servlet.Filter#destroy() */ @Override public void destroy() { } /* * (non-Javadoc) * * @see * org.springframework.security.access.intercept.AbstractSecurityInterceptor * #getSecureObjectClass() */ @Override public Class<?> getSecureObjectClass() { // 下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误 return FilterInvocation.class; } }
MySecurityMetadataSource.java:
/** * */ package com.simonsw.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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 com.simonsw.base.entity.Role; import com.simonsw.base.entity.RoleResource; import com.simonsw.base.entity.UserRole; import com.simonsw.base.entity.Users; import com.simonsw.base.service.RoleResourceService; import com.simonsw.base.service.UserRoleService; import com.simonsw.base.service.UserService; /** * @author Simon Lv * @since Oct 31, 2013 */ public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired protected UserService userService; @Autowired protected UserRoleService userRoleService; @Autowired protected RoleResourceService roleResourceService; private Logger logger = LoggerFactory.getLogger(getClass()); private static LinkedHashMap<String, Collection<ConfigAttribute>> resourceMap = null; /* * (non-Javadoc) * * @see * org.springframework.security.access.SecurityMetadataSource#getAttributes * (java.lang.Object) */ @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { String requestURL = ((FilterInvocation) object).getRequestUrl(); logger.debug("[MySecurityMetadataSource] 请求地址 ===> " + ((FilterInvocation) object).getRequestUrl()); if (null == resourceMap) { loadResourceDefine(); logger.debug("[MySecurityMetadataSource] 我需要的认证 ==> " + resourceMap.toString()); } for (Map.Entry<String, Collection<ConfigAttribute>> entry : resourceMap .entrySet()) { logger.debug("[MySecurityMetadataSource] entry.getKey() ===> " + entry.getKey()); logger.debug("[MySecurityMetadataSource] request ===> " + requestURL); if (entry.getKey().equals(requestURL)) { return entry.getValue(); } } return null; } /** * Load all resource */ private void loadResourceDefine() { resourceMap = new LinkedHashMap<String, Collection<ConfigAttribute>>(); Map<String, String> resource = getResource(); for (Map.Entry<String, String> entry : resource.entrySet()) { Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>(); configAttributes.add(new SecurityConfig(entry.getValue())); resourceMap.put(entry.getKey(), configAttributes); } } /** * 加载所有资源与权限的关系 * * @return */ private Map<String, String> getResource() { Map<String, String> resourceMap = new HashMap<String, String>(); List<Users> users = userService.findAll(); List<UserRole> userRoles; Role role; List<RoleResource> roleResources; userfor: for (Users user : users) { logger.debug("userId => " + user.getUserid()); userRoles = userRoleService.getUserRoleByUserId(user); if (userRoles == null) { logger.debug("userfor => break!!!!"); break userfor; } rolefor: for (UserRole userRole : userRoles) { role = userRole.getRole(); roleResources = roleResourceService.getUserRoleByRoleId(role); if (roleResources == null) { break rolefor; } for (RoleResource roleResource : roleResources) { String url = roleResource.getResource().getValue(); if (!resourceMap.containsKey(url)) { resourceMap.put(url, role.getName()); } else { String roleName = resourceMap.get(url); resourceMap.put(url, roleName + "," + role.getName()); } } } } return resourceMap; } /* * (non-Javadoc) * * @see org.springframework.security.access.SecurityMetadataSource# * getAllConfigAttributes() */ @Override public Collection<ConfigAttribute> getAllConfigAttributes() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * * @see * org.springframework.security.access.SecurityMetadataSource#supports(java * .lang.Class) */ @Override public boolean supports(Class<?> arg0) { // TODO Auto-generated method stub return true; } }
MyUsernamePasswordAuthenticationFilter.java:
/** * */ package com.simonsw.security; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.simonsw.base.entity.Users; import com.simonsw.base.service.UserService; import com.simonsw.common.util.CipherUtil; import com.simonsw.common.util.StringUtils; /** * @author Simon Lv * @since Oct 31, 2013 */ public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Autowired protected UserService userService; private Logger logger = LoggerFactory.getLogger(getClass()); public static final String VALIDATE_CODE = "validateCode"; public static final String USERNAME = "username"; public static final String PASSWORD = "password"; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } // checkValidateCode(request); String username = obtainUsername(request); logger.debug("[MyUsernamePasswordAuthenticationFilter] username ==> "+ username); String password = CipherUtil.generatePassword(obtainPassword(request)); logger.debug("[MyUsernamePasswordAuthenticationFilter] password ==> "+ password); // 验证用户账号与密码是否对应 username = username.trim(); Users users = userService.getUserByName(username); if (users == null || !users.getPassword().equals(password)) { throw new AuthenticationServiceException( "password or username is notEquals"); } // UsernamePasswordAuthenticationToken实现 Authentication UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Place the last username attempted into HttpSession for views // 允许子类设置详细属性 setDetails(request, authRequest); // 运行UserDetailsService的loadUserByUsername 再次封装Authentication return this.getAuthenticationManager().authenticate(authRequest); } protected void checkValidateCode(HttpServletRequest request) { HttpSession session = request.getSession(); String sessionValidateCode = obtainSessionValidateCode(session); // 让上一次的验证码失效 session.setAttribute(VALIDATE_CODE, null); String validateCodeParameter = obtainValidateCodeParameter(request); if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) { throw new AuthenticationServiceException("validateCode.notEquals"); } } private String obtainValidateCodeParameter(HttpServletRequest request) { Object obj = request.getParameter(VALIDATE_CODE); return null == obj ? "" : obj.toString(); } protected String obtainSessionValidateCode(HttpSession session) { Object obj = session.getAttribute(VALIDATE_CODE); return null == obj ? "" : obj.toString(); } @Override protected String obtainUsername(HttpServletRequest request) { Object obj = request.getParameter(USERNAME); return null == obj ? "" : obj.toString(); } @Override protected String obtainPassword(HttpServletRequest request) { Object obj = request.getParameter(PASSWORD); return null == obj ? "" : obj.toString(); } }
具体的security.xml配置信息:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns="http://www.springframework.org/schema/security" 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.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <debug /> <global-method-security pre-post-annotations="enabled" /> <!-- 此目录下不需要过滤 --> <http pattern="/js/**" security="none" /> <http pattern="/css/**" security="none" /> <http pattern="/images/**" security="none" /> <http pattern="/login" security="none" /> <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint"> <!-- if no limit, go to 403 page --> <access-denied-handler error-page="/403" /> <!-- Logout --> <logout invalidate-session="true" logout-url="/logout" success-handler-ref="myLogoutSuccessHandler" /> <!-- 实现免登陆验证 --> <remember-me /> <session-management invalid-session-url="/"> <concurrency-control max-sessions="10" error-if-maximum-exceeded="true" /> </session-management> <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" /> <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" /> </http> <!-- 登录验证器 --> <beans:bean id="loginFilter" class="com.simonsw.security.MyUsernamePasswordAuthenticationFilter"> <!-- 处理登录 --> <beans:property name="filterProcessesUrl" value="/user/login"></beans:property> <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> <beans:property name="authenticationManager" ref="myAuthenticationManager" /> </beans:bean> <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="defaultTargetUrl" value="/"></beans:property> </beans:bean> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/"></beans:property> </beans:bean> <!-- 认证过滤器 --> <beans:bean id="securityFilter" class="com.simonsw.security.MySecurityFilter"> <!-- 用户拥有的权限 --> <beans:property name="authenticationManager" ref="myAuthenticationManager" /> <!-- 用户是否拥有所请求资源的权限 --> <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" /> <!-- 资源与权限对应关系 --> <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" /> </beans:bean> <!-- 实现了UserDetailsService的Bean --> <authentication-manager alias="myAuthenticationManager"> <authentication-provider user-service-ref="myAuthenticationManagers" /> </authentication-manager> <beans:bean id="myAccessDecisionManager" class="com.simonsw.security.MyAccessDecisionManager"></beans:bean> <beans:bean id="mySecurityMetadataSource" class="com.simonsw.security.MySecurityMetadataSource"> </beans:bean> <beans:bean id="myAuthenticationManagers" class="com.simonsw.security.MyAuthenticationManager"> </beans:bean> <beans:bean id="myLogoutSuccessHandler" class="com.simonsw.security.MyLogoutSuccessHandler" /> <!-- 未登录的切入点 --> <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <beans:property name="loginFormUrl" value="/login"></beans:property> </beans:bean> </beans:beans>
最后一定要在web.xml中配置加上:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-security.xml</param-value> </context-param>
<!-- Spring Security Session --> <listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener> <!-- Spring security Filter --> <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>
启动Tomcat,登录正常工作。
具体想看源代码,请访问GitHub网站上的源代码:
https://github.com/simon5408/DummyProj
顺便说一下,得到源代码以后,需要数据库支持,具体的数据库结构及数据请看附件。