原理:就是拦截器拦截请求
(只可意会,明白其中原理,代码太多,model和dao没有全部粘贴,如果不符合你的需求,不必往下看)
pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zmc</groupId>
<artifactId>SpringSecurityDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringSecurityDemo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java-version>1.7</java-version>
<org.springframework-version>3.2.2.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.1</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<!-- Spring security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
首先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>
在spring的安全配置问件中配置问件applicationContext-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"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<!-- 登录页面、提示无权限、申请权限页面不过滤 -->
<http pattern="/login.do**" security="none" />
<http pattern="/common/**" security="none" />
<http pattern="/register.do**" security="none" />
<http use-expressions="true" auto-config="true"> <!-- 当访问被拒绝时,会转到403.jsp -->
<anonymous enabled="true" username="guest"
granted-authority="ROLE_ANONYMOUS" />
<!-- <form-login login-page="/login.do?method=index"
default-target-url="/login.do?method=loginok"
always-use-default-target="true" authentication-failure-url="/login.do?method=loginFailure" />-->
<form-login login-page="/login.do?method=index" authentication-success-handler-ref="expaiSuccessHandler"
always-use-default-target="false" authentication-failure-handler-ref="expaiSuccessHandler" />
<logout logout-url="/j_spring_security_logout"
invalidate-session="true" success-handler-ref="myLogoutSuccessHandler"/>
<!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />
<!-- <session-management invalid-session-url="/login.do?method=timeout">
<concurrency-control max-sessions="10"
error-if-maximum-exceeded="true" />
</session-management>
-->
<session-management>
<concurrency-control max-sessions="10"
error-if-maximum-exceeded="true" />
</session-management>
<remember-me key="e37f4b31-0c45-11dd-bd0b-0800200c9a66" user-service-ref="myUserDetailService"/>
</http>
<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,我们的所有控制将在这三个类中实现,解释详见具体配置 -->
<beans:bean id="myFilter"
class="com.wxx.topicmanager.security.MyFilterSecurityInterceptor">
<!-- 用户是否拥有所请求资源的权限 -->
<beans:property name="authenticationManager" ref="authenticationManager" />
<!-- 用户拥有的权限 -->
<beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
<!-- 资源与权限对应关系 -->
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailService">
<password-encoder ref="myPasswordEncode">
<!-- <salt-source user-property="salt" /> -->
</password-encoder>
</authentication-provider>
<!-- 如果用户的密码采用加密的话,可以加点“盐” <password-encoder hash="md5" /> -->
</authentication-manager>
<beans:bean id="myPasswordEncode"
class="com.wxx.topicmanager.security.MyPasswordEncode">
<beans:constructor-arg name="algorithm" value="md5"></beans:constructor-arg>
</beans:bean>
<beans:bean id="myUserDetailService"
class="com.wxx.topicmanager.security.MyUserDetailsService">
</beans:bean>
<beans:bean id="securityMetadataSource"
class="com.wxx.topicmanager.security.MyInvocationSecurityMetadataSource"
scope="prototype">
</beans:bean>
<beans:bean id="myAccessDecisionManager"
class="com.wxx.topicmanager.security.MyAccessDecisionManager" scope="prototype">
</beans:bean>
<beans:bean id="expaiSuccessHandler" class="com.wxx.topicmanager.security.MyAuthenticationSuccessHandler"></beans:bean>
<beans:bean id="myLogoutSuccessHandler" class="com.wxx.topicmanager.security.MyLogoutSuccessHandler"></beans:bean>
</beans:beans>
相关引入类
MyFilterSecurityInterceptor.java
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.apache.log4j.Logger;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
implements Filter {
/**
* 记录调试信息的Logger
*/
private static Logger m_Logger = Logger
.getLogger(MyFilterSecurityInterceptor.class);
private FilterInvocationSecurityMetadataSource securityMetadataSource;
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
// ~ Methods
// ========================================================================================================
/** */
/**
* Method that is actually called by the filter chain. Simply delegates to
* the {@link #invoke(FilterInvocation)} method.
*
* @param request
* the servlet request
* @param response
* the servlet response
* @param chain
* the filter chain
*
* @throws IOException
* if the filter chain fails
* @throws ServletException
* if the filter chain fails
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
m_Logger.debug("MyFilterSecurityInterceptor.doFilter: I am called.");
m_Logger.debug("AccessDecisionManager:"
+ this.getAccessDecisionManager());
Authentication oAuthentication = SecurityContextHolder.getContext()
.getAuthentication();
m_Logger.debug("oAuthentication:" + oAuthentication);
// 从session中获取登录用户角色信息,添加到Spring security 权限中
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// Spring security没有登录,可以根据实际项目需要执行自己特定的逻辑,默认是不需要的.
// System.out.println("用户没有登录.");
}
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 {
// 在执行doFilter之前,进行权限的检查,而具体的实现已经交给accessDecisionManager
super.setAccessDecisionManager(getAccessDecisionManager());
InterceptorStatusToken token = beforeInvocation(fi);
fi.getRequest().setAttribute(
"__spring_security_filterSecurityInterceptor_filterApplied",
true);
try {
m_Logger.debug("before MyFilterSecurityInterceptor.invoke."
+ "accessDecisionManager:" + getAccessDecisionManager());
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
m_Logger.debug("after MyFilterSecurityInterceptor.invoke.");
} catch (RuntimeException ex) {
ex.printStackTrace();
throw ex;
} finally {
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 {
}
}
MyPasswordEncode.java
import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;
import com.wxx.project.util.PasswordUtil;
public class MyPasswordEncode extends MessageDigestPasswordEncoder {
public MyPasswordEncode(String algorithm) {
super(algorithm);
}
@Override
// 如果返回true,则验证通过。
public boolean isPasswordValid(String savePass, String submitPass,
Object salt) {
// return savePass.equalsIgnoreCase(Util.MD5WithSalt(submitPass,
// salt.toString()));
String md5SubmitPass = "";
try {
md5SubmitPass = PasswordUtil.generatePassword(submitPass);
} catch (Exception ex) {
throw new RuntimeException("MD5加密失败,失败原因:" + ex);
}
return savePass.equalsIgnoreCase(md5SubmitPass);
}
}
MyUserDetailsService.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
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.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.topicmanager.bswuser.BswUser;
import com.wxx.topicmanager.bswuser.BswUserService;
import com.wxx.topicmanager.user.AuthorityService;
import com.wxx.topicmanager.user.TRole;
/**
* 实现Spring acegi权限模型要求的用户实现接口
*
* @author wang.chungang@trs.com.cn
*
*/
@Service
public class MyUserDetailsService implements UserDetailsService {
/**
* 记录调试信息的Logger
*/
private static Logger m_Logger = Logger.getLogger(MyUserDetailsService.class);
@Autowired
private BswUserService bswUserService;
@Autowired
private AuthorityService authorityService;
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetailsService#
* loadUserByUsername(java.lang.String)
*/
public UserDetails loadUserByUsername(String _sUserName)
throws UsernameNotFoundException {
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_BSW);
m_Logger.debug("MyUseDetailsService.loadUserByUsername(): I am called. UserName["
+ _sUserName + "]");
BswUser aUser = bswUserService.findByName(_sUserName);
if (aUser == null) {
throw new UsernameNotFoundException("用户[" + _sUserName + "]不存在");
}
// 将用户转换成Spring规范的用户
User aSprignUser = convertToSpringUser(aUser);
return aSprignUser;
}
/**
* 将系统用户翻译成spring用户
*
* @param aUser
* @return
*/
private User convertToSpringUser(BswUser aUser) {
// 生成用户的全部角色集合
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
List<TRole> listRoles = authorityService.getRoles(aUser);
for (TRole tRole : listRoles) {
String sRoleName = tRole.getRoleName();
auths.add(new GrantedAuthorityImpl(sRoleName));
}
// 所有用户均添加访客角色
// GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl(
// Constants.defaultRolename);
// auths.add(auth2);
User aSpringUser = new User(aUser.getLoginname(), aUser.getPassword(),
true, true, true, true, auths);
return aSpringUser;
}
}
MyInvocationSecurityMetadataSource.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
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.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.CMyString;
import com.wxx.topicmanager.user.RoleService;
import com.wxx.topicmanager.user.TPower;
import com.wxx.topicmanager.user.TRole;
/**
* 此类用于角色对应的资源数据的管理
*
*/
@ComponentScan
public class MyInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
Logger logger = Logger.getLogger(getClass());
private PathMatcher urlMatcher = new AntPathMatcher();
// 角色
@Autowired
private RoleService roleService;
// 解析Url的匹配器,框架提供两种选择:一个是AntUrlPathMatcher,另一个是正则匹配
// private UrlMatcher urlMatcher = new RegexUrlPathMatcher();
// private UrlMatcher urlMatcher = new AntUrlPathMatcher();
// 该map集合用于存放资源-角色的对应关系
public static Map<String, Collection<ConfigAttribute>> resourceMap = null;
// /**
// * 该构造方法有spring容器调用
// */
// public MyInvocationSecurityMetadataSource() {
// this.loadResourceDefine();
// }
// TODO 通过定时任务将数据库中的资源和角色对应关系同步到系统内存常量resourceMap,保证管理员动态修改资源和角色及时生效
/**
* 加载相关的资源权限的定义,将定义的权限构建ConfigAttribute对象,存入
* Collection<T>泛型集合中,并将该集合作为value。将角色拥有的资源的url作为key,由此
* 构建一个map集合将资源对应的角色信息对应存放。
*/
public void loadResourceDefine() {
long startTime = System.currentTimeMillis();
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
// roleManager = (RoleManager) this.getBean("roleManager");
List<TRole> roles = roleService.getAll(); // 获取所有的角色
// 此集合的作用是存放角色信息的ConfigAttribute实现类对象
Collection<ConfigAttribute> attributes = null;
for (TRole role : roles) {
ConfigAttribute attribute = new SecurityConfig(role.getRoleName());
// 遍历角色对应的权限
List<TPower> listPowers = roleService.getPowers(role);
for (TPower power : listPowers) {
// 遍历每个Power
String sPowerURL = power.getPowerURL();
if (StringUtils.isNotEmpty(sPowerURL)) {
if (resourceMap.containsKey(sPowerURL)) {
Collection<ConfigAttribute> value = resourceMap
.get(sPowerURL);
value.add(attribute);
resourceMap.put(sPowerURL, value);
} else {
attributes = new ArrayList<ConfigAttribute>();
attributes.add(attribute);
resourceMap.put(sPowerURL, attributes);
}
}
}
}
long endTime = System.currentTimeMillis();
logger.debug("resourceMap:" + resourceMap);
logger.info("初始化资源URL和角色对应关系耗时" + (endTime - startTime) / 1000.0
+ "秒,包含记录" + resourceMap.size() + "条");
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public Collection<ConfigAttribute> getAttributes(Object arg)
throws IllegalArgumentException {
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
if (resourceMap == null) {
loadResourceDefine();
}
long startTime = System.currentTimeMillis();
int count = 0;// url匹配次数
try {
Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
// 获取URL的web应用程序的特定片段,不包括任何服务器的名称、上下文路径或servlet的路径
String url = ((FilterInvocation) arg).getRequestUrl(); // 当前访问的URL
Set<String> keySet = resourceMap.keySet();
for (String key : keySet) {
count++;
// pathMatchesUrl方法判断请求的URL是否匹配,如果有匹配的,则返回保存有角色信息的ConfigAttribute对象的集合
// 支持用-号作为路径的排除
String[] arURLPatterns = CMyString.split(key, ";");
boolean bExcluded = false;
for (String sURLPattern : arURLPatterns) {
if (!sURLPattern.startsWith("-"))
continue;
// 验证是否是排除当前角色
String sExcludeURLPattern = sURLPattern.substring(1,
sURLPattern.length());
if (urlMatcher.match(sExcludeURLPattern, url)) {
bExcluded = true;
break;
}
}
if (bExcluded)
continue;
if (!bExcluded) {
// 没有被排除,继续验证当前角色是否匹配
for (String sURLPattern : arURLPatterns) {
if (sURLPattern.startsWith("-"))
continue;
if (urlMatcher.match(sURLPattern, url)) {
Collection<ConfigAttribute> tempAttributes = resourceMap
.get(key);
logger.debug("sURLPattern:" + sURLPattern
+ ";url:" + url + ";tempAttributes:"
+ tempAttributes);
attributes.addAll(tempAttributes);
}
}
}
}
// 返回
if (attributes.isEmpty()) {
// 如果没有匹配的返回null,表示此url直接放行
return null;
}
// 返回用户的所有的角色
logger.debug("url:[" + url + "]; roles:[" + attributes + "]");
return attributes;
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException(e);
} finally {
long costTime = System.currentTimeMillis() - startTime;
logger.debug("权限url匹配" + count + "次,耗时:" + costTime / 1000.0 + "秒");
}
}
public boolean supports(Class<?> arg0) {
return true;
}
}
MyAccessDecisionManager.java
import java.util.Collection;
import java.util.Iterator;
import org.apache.log4j.Logger;
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;
/**
* 自定义决策管理器,用于判断用户请求的资源是否和用户角色匹配
* SecurityAccessDecisionManager实现AccessDecisionManager接口,拥有spring security 访问控制
*
*/
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
* 记录调试信息的Logger
*/
private static Logger m_Logger = Logger.getLogger(MyAccessDecisionManager.class);
/**
* 此构造方法有spring容器调用
*/
public MyAccessDecisionManager() {
}
/**
* 传递参数的访问控制决策。如果访问被拒绝作为身份验证不持有所需的权限或ACL权限,则抛出AccessDeniedException 异常
*
* @param authentication
* 该方法的调用者。Authentication接口表示验证身份的主体令牌,由此可以获取 已经验证用户的用户及角色信息
* @param obj
* 称之为担保对象。可以是一个要求保护的方法或者URL
* @param attributes
* 与保护对象相关联的配置属性.ConfigAttribute接口是用来存储安全系统相关的配置属性。
* 由FilterInvocationSecurityMetadataSource实现类中获取
*/
public void decide(Authentication authentication, Object obj,
Collection<ConfigAttribute> attributes)
throws AccessDeniedException, InsufficientAuthenticationException {
// 如果请求url不需要权限控制,则所有用户均可以访问
if (attributes == null) {
return;
}
Iterator<ConfigAttribute> configIterator = attributes.iterator();
// 获取当前登录用户具有的角色信息
Collection<GrantedAuthority> grantedAuthorities = (Collection<GrantedAuthority>) authentication
.getAuthorities();
m_Logger.debug("当前页面需要角色[attributes=" + attributes
+ "];用户授权角色[grantedAuthorities=" + grantedAuthorities + "]");
// 对请求的资源所需权限进行遍历。
while (configIterator.hasNext()) {
ConfigAttribute attribute = configIterator.next();
String needRole = ((SecurityConfig) attribute).getAttribute();
// 对当前登录用户的角色信息进行遍历
for (GrantedAuthority authority : grantedAuthorities) {
// 获取当前登录用户的角色名称并与其请求资源所需角色的名称进行比对。如果匹配返回
if (needRole.equals(authority.getAuthority())) {
return;
}
}
}
/*
* 如果当前登录用户的角色名次与其请求资源所需角色的名称不匹配,抛出AccessDeniedException异常, 最终有spring
* security处理此异常,并根据安全配置文件的配置返回相关错误提示页面
*/
throw new AccessDeniedException("no right");
}
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> cls) {
// TODO Auto-generated method stub
return true;
}
}
MyAuthenticationSuccessHandler.java
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.RequestUtils;
import com.wxx.topicmanager.bswuser.BswUser;
import com.wxx.topicmanager.bswuser.BswUserService;
import com.wxx.topicmanager.entities.Operlog;
import com.wxx.topicmanager.service.OperlogService;
import com.wxx.topicmanager.util.RemoteAddrUtil;
public class MyAuthenticationSuccessHandler implements
AuthenticationSuccessHandler, AuthenticationFailureHandler {
@Autowired
private OperlogService operlogService;
@Autowired
private BswUserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication auth)
throws IOException, ServletException {
RequestUtils requestUtils = new RequestUtils(request);
// 记录日志
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
User loginUser = (User) auth.getPrincipal();
Operlog operlog = new Operlog();
operlog.setOpertype(Operlog.TYPE_LOGIN);
operlog.setOperdesc("用户[" + loginUser.getUsername() + "]登录成功!");
operlog.setRemoteip(RemoteAddrUtil.getRemoteAddr(request));
try {
operlogService.saveUserOperLog(operlog, loginUser.getUsername());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int nMode = requestUtils.getParameterAsInt("mode", 0);
if (nMode == 1) {
response.sendRedirect("login.do?method=loginok");
// request.getRequestDispatcher("/WEB-INF/jsps/login/login-ok.jsp").forward(request,
// response);
} else if (nMode == 2) {
response.sendRedirect("client/client.do?method=loginok");
/*response.setContentType("text/html");
// 设置字符编码为UTF-8, 这样支持汉字显示
response.setCharacterEncoding("GBk");
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_BSW);
BswUser bswUser = userService.findByName(loginUser.getUsername());
response.getWriter().println(
"{\"rtState\":0,\"rtMsg\":\"登录成功\",\"rtData\":{\"userName\":\""
+ bswUser.getLoginname() + "\",\"id\":\""
+ bswUser.getUserid() + "\",\"cnName\":\""
+ bswUser.getUsername()
+ "\",\"isLeader\":false,\"mac\":\""
+ bswUser.getMac() + "\"}}");*/
} else {
response.sendRedirect("index.do?method=index");
}
}
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION",
exception);
RequestUtils requestUtils = new RequestUtils(request);
int nMode = requestUtils.getParameterAsInt("mode", 0);
if (nMode == 2) {
request.getRequestDispatcher(
"login.do?method=loginFailure&mode=" + nMode).forward(
request, response);
} else {
response.sendRedirect("login.do?method=loginFailure&mode=" + nMode);
}
}
}
MyLogoutSuccessHandler.java
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.RequestUtils;
import com.wxx.topicmanager.entities.Operlog;
import com.wxx.topicmanager.service.OperlogService;
import com.wxx.topicmanager.util.RemoteAddrUtil;
public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler
implements LogoutSuccessHandler {
@Autowired
private OperlogService operlogService;
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
RequestUtils requestUtils = new RequestUtils(request);
int nMode = requestUtils.getParameterAsInt("mode", 0);
User loginUser = (User) authentication.getPrincipal();
// 记录日志
CustomerContextHolder
.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
Operlog operlog = new Operlog();
operlog.setOpertype(Operlog.TYPE_LOGOUT);
operlog.setOperdesc("用户退出");
operlog.setRemoteip(RemoteAddrUtil.getRemoteAddr(request));
try {
operlogService.saveUserOperLog(operlog, loginUser.getUsername());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (nMode == 2) {
response.setContentType("text/html");
// 设置字符编码为UTF-8, 这样支持汉字显示
response.setCharacterEncoding("GBk");
try {
response.getWriter().println(
"{\"rtState\":0,\"rtMsg\":\"已注销\"}");
} catch (IOException e) {
e.printStackTrace();
}
} else if (nMode == 3) {
response.sendRedirect("mobile-login.do?method=login");
} else {
super.onLogoutSuccess(request, response, authentication);
}
}
}
也可以简化applicationContext-security.xml中的FilterSecurityInterceptor
<?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/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http pattern="/login.jsp" security="none"></http>
<http auto-config="false">
<form-login login-page="/login.jsp" default-target-url="/index.jsp"
authentication-failure-url="/login.jsp?error=true" />
<logout invalidate-session="true" logout-success-url="/login.jsp"
logout-url="/j_spring_security_logout" />
<!-- 通过配置custom-filter来增加过滤器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。 -->
<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<!-- 数据源 -->
<beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 此为c3p0在spring中直接配置datasource c3p0是一个开源的JDBC连接池 -->
<beans:property name="driverClass" value="com.mysql.jdbc.Driver" />
<beans:property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/springsecuritydemo?useUnicode=true&characterEncoding=UTF-8" />
<beans:property name="user" value="root" />
<beans:property name="password" value="" />
<beans:property name="maxPoolSize" value="50"></beans:property>
<beans:property name="minPoolSize" value="10"></beans:property>
<beans:property name="initialPoolSize" value="10"></beans:property>
<beans:property name="maxIdleTime" value="25000"></beans:property>
<beans:property name="acquireIncrement" value="1"></beans:property>
<beans:property name="acquireRetryAttempts" value="30"></beans:property>
<beans:property name="acquireRetryDelay" value="1000"></beans:property>
<beans:property name="testConnectionOnCheckin" value="true"></beans:property>
<beans:property name="idleConnectionTestPeriod" value="18000"></beans:property>
<beans:property name="checkoutTimeout" value="5000"></beans:property>
<beans:property name="automaticTestTable" value="t_c3p0"></beans:property>
</beans:bean>
<beans:bean id="builder" class="com.zmc.demo.JdbcRequestMapBulider">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="resourceQuery"
value="select re.res_string,r.name from role r,resc re,resc_role rr where
r.id=rr.role_id and re.id=rr.resc_id" />
</beans:bean>
<!-- 认证过滤器 -->
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<!-- 用户拥有的权限 -->
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<!-- 用户是否拥有所请求资源的权限 -->
<beans:property name="authenticationManager" ref="authenticationManager" />
<!-- 资源与权限对应关系 -->
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<!-- 授权管理器 -->
<beans:bean class="com.zmc.demo.MyAccessDecisionManager" id="accessDecisionManager">
</beans:bean>
<!--认证管理-->
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" id="usersService"
users-by-username-query="select username,password,status as enabled from user where username = ?"
authorities-by-username-query="select user.username,role.name from user,role,user_role
where user.id=user_role.user_id and
user_role.role_id=role.id and user.username=?" />
</authentication-provider>
</authentication-manager>
<!--自定义的切入点-->
<beans:bean id="securityMetadataSource"
class="com.zmc.demo.MyFilterInvocationSecurityMetadataSource">
<beans:property name="builder" ref="builder"></beans:property>
</beans:bean>
</beans:beans>
相关代码
MyFilterInvocationSecurityMetadataSource .java
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.RequestMatcher;
/**
* @classname MyFilterInvocationSecurityMetadataSource
* @author ZMC
* @time 2017-1-10
*/
public class MyFilterInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource, InitializingBean {
private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = null;
// 资源权限集合
private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
//查找数据库权限和资源关系
private JdbcRequestMapBulider builder;
/*
* (non-Javadoc)
* @see
* org.springframework.security.access.SecurityMetadataSource#getAttributes
* (java.lang.Object)
* 更具访问资源的地址查找所需要的权限
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object)
.getRequest();
Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
.entrySet()) {
if (entry.getKey().matches(request)) {
attrs = entry.getValue();
break;
}
}
return attrs;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.access.SecurityMetadataSource#
* getAllConfigAttributes()
* 获取所有的权限
*/
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
.entrySet()) {
allAttributes.addAll(entry.getValue());
}
System.out.println("总共有这些权限:"+allAttributes.toString());
return allAttributes;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.access.SecurityMetadataSource#supports(java
* .lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
//绑定requestMap
protected Map<RequestMatcher, Collection<ConfigAttribute>> bindRequestMap() {
return builder.buildRequestMap();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
this.requestMap = this.bindRequestMap();
}
public void refreshResuorceMap() {
this.requestMap = this.bindRequestMap();
}
//get方法
public JdbcRequestMapBulider getBuilder() {
return builder;
}
//set方法
public void setBuilder(JdbcRequestMapBulider builder) {
this.builder = builder;
}
}
MyAccessDecisionManager .java
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.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
/**
* @classname MyAccessDecisionManager
* @author ZMC
* @time 2017-1-10
*
*/
public class MyAccessDecisionManager implements AccessDecisionManager {
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
* 该方法决定该权限是否有权限访问该资源,其实object就是一个资源的地址,authentication是当前用户的
* 对应权限,如果没登陆就为游客,登陆了就是该用户对应的权限
*/
@Override
public void decide(Authentication authentication, Object object,
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();
System.out.println("访问"+object.toString()+"需要的权限是:" + needPermission);
//用户所拥有的权限authentication
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for(GrantedAuthority ga : authorities) {
if(needPermission.equals(ga.getAuthority())) {
return;
}
}
}
//没有权限
throw new AccessDeniedException(" 没有权限访问! ");
}
@Override
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
}