近期对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
顺便说一下,得到源代码以后,需要数据库支持,具体的数据库结构及数据请看附件。