Spring Security简单Demo
1.创建项目且引入相关的.jar文件
spring-security-acl-2.0.5.jar
spring-security-core-2.0.5.jar
spring-security-taglibs-2.0.5.jar
2.创建相应的实体类:用户、角色、资源
2.1用户类:User.java需要实现UserDetails接口,具体内容如下:
package com.zsw.entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Proxy;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
/**
* 用户类
* @author Administrator
*/
@Entity
@Proxy(lazy = false)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public
class User
implements UserDetails {
private
static
final
long serialVersionUID = 8026813053768023527L;
/**
* 编号ID
*/
@Id
@GeneratedValue
private Integer id;
private
String name;
//用户名
private
String password;
//密码
private
boolean disabled;
//是否关闭
/**
* 用户所有的角色
*/
@ManyToMany(targetEntity = Role.
class, fetch = FetchType.EAGER)
@JoinTable(name =
"user_role", joinColumns = @JoinColumn(name =
"user_id"), inverseJoinColumns = @JoinColumn(name =
"role_id"))
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Role> roles;
@Transient
private Map<
String, List<Resource>> roleResources;
/**
* The default constructor
*/
public User() {
}
/**
* 根据User返回这个User所拥有的权限列表
*/
public GrantedAuthority[] getAuthorities() {
List<GrantedAuthority> grantedAuthorities =
new ArrayList<GrantedAuthority>(roles.size());
for(Role role : roles) {
grantedAuthorities.add(
new GrantedAuthorityImpl(role.getName()));
}
return grantedAuthorities.toArray(
new GrantedAuthority[roles.size()]);
}
/**
* Returns the authorites string
*
* eg.
* downpour --- ROLE_ADMIN,ROLE_USER
* robbin --- ROLE_ADMIN
*
* @return
*/
public
String getAuthoritiesString() {
List<
String> authorities =
new ArrayList<
String>();
for(GrantedAuthority authority :
this.getAuthorities()) {
authorities.add(authority.getAuthority());
}
return StringUtils.join(authorities,
",");
}
public
String getPassword() {
return password;
}
public
String getUsername() {
return name;
}
public
boolean isAccountNonExpired() {
return true;
}
public
boolean isAccountNonLocked() {
return true;
}
public
boolean isCredentialsNonExpired() {
return true;
}
public
boolean isEnabled() {
return !disabled;
}
public Integer getId() {
return id;
}
public
String getName() {
return name;
}
public
boolean isDisabled() {
return disabled;
}
public Set<Role> getRoles() {
return roles;
}
/**
* 在User对象中设置一个缓存机制,在第一次取的时候,
* 通过遍历User所有的Role,获取相应的Resource信息。
* @return
*/
public Map<
String, List<Resource>> getRoleResources() {
// init roleResources for the first time
if(
this.roleResources == null) {
this.roleResources =
new HashMap<
String, List<Resource>>();
for(Role role :
this.roles) {
String roleName = role.getName();
Set<Resource> resources = role.getResources();
for(Resource resource : resources) {
String key = roleName +
"_" + resource.getType();
if(!
this.roleResources.containsKey(key)) {
this.roleResources.put(key,
new ArrayList<Resource>());
}
this.roleResources.get(key).add(resource);
}
}
}
return
this.roleResources;
}
public
void setId(Integer id) {
this.id = id;
}
public
void setName(
String name) {
this.name = name;
}
public
void setPassword(
String password) {
this.password = password;
}
public
void setDisabled(
boolean disabled) {
this.disabled = disabled;
}
public
void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
其中,接口UserDetails定义的方法如下:
public
interface UserDetails
extends Serializable {
GrantedAuthority[] getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
2.2角色:Role.java具体的内容如下:
package com.zsw.entity;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* 角色
* @author Administrator
*/
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public
class Role {
@Id
@GeneratedValue
private Integer id;
private
String name;
private
String description;
@ManyToMany(targetEntity = Resource.
class, fetch = FetchType.EAGER)
@JoinTable(name =
"role_resource", joinColumns = @JoinColumn(name =
"role_id"), inverseJoinColumns = @JoinColumn(name =
"resource_id"))
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Resource> resources;
public Role() {
}
public Integer getId() {
return id;
}
public
String getName() {
return name;
}
public
String getDescription() {
return description;
}
public Set<Resource> getResources() {
return resources;
}
public
void setId(Integer id) {
this.id = id;
}
public
void setName(
String name) {
this.name = name;
}
public
void setDescription(
String description) {
this.description = description;
}
public
void setResources(Set<Resource> resources) {
this.resources = resources;
}
}
2.3资源Resource.java具体的内容如下:
package com.zsw.entity;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* 资源
* Resource可能分成多种类型,比如MENU,URL,METHOD等等
*/
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public
class Resource {
@Id
@GeneratedValue
private Integer id;
private
String type;
private
String value;
@ManyToMany(mappedBy =
"resources", targetEntity = Role.
class, fetch = FetchType.EAGER)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Role> roles;
public Resource() {
}
/**
* 获取资源中所有角色名称
* @return
*/
@Transient
public
String getRoleAuthorities() {
List<
String> roleAuthorities =
new ArrayList<
String>();
for(Role role : roles) {
roleAuthorities.add(role.getName());
}
return StringUtils.join(roleAuthorities,
",");
}
public Integer getId() {
return id;
}
public
String getType() {
return type;
}
public
String getValue() {
return value;
}
public Set<Role> getRoles() {
return roles;
}
public
void setId(Integer id) {
this.id = id;
}
public
void setType(
String type) {
this.type = type;
}
public
void setValue(
String value) {
this.value = value;
}
public
void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
3.定义一个接口SecurityManager.java用于获取所有的资源信息
package com.zsw.security;
import java.util.Map;
/**
* 安全管理
* @author Administrator
*/
public
interface SecurityManager {
public Map<
String,
String> loadUrlAuthorities();
}
其具体的实现类如下:
package com.zsw.security.support;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.zsw.entity.Resource;
import com.zsw.entity.User;
import com.zsw.security.SecurityManager;
@Service(
"securityManager")
public
class SecurityManagerSupport
extends HibernateDaoSupport
implements UserDetailsService, SecurityManager {
@Autowired
public
void init(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
/**
* 根据用户名获取用户对象
* 实现了UserDetailsService接口中的loadUserByUsername方法
*/
public UserDetails loadUserByUsername(
String userName)
throws UsernameNotFoundException, DataAccessException {
List<User> users = getHibernateTemplate().find(
"FROM User user WHERE user.name = ? AND user.disabled = false", userName);
if(users.isEmpty()) {
throw
new UsernameNotFoundException(
"User " + userName +
" has no GrantedAuthority");
}
return users.get(0);
}
/**
* 获取所有资源,对应所有的角色
*/
public Map<
String,
String> loadUrlAuthorities() {
Map<
String,
String> urlAuthorities =
new HashMap<
String,
String>();
List<Resource> urlResources = getHibernateTemplate().find(
"FROM Resource resource WHERE resource.type = ?",
"URL");
for(Resource resource : urlResources) {
urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());
}
return urlAuthorities;
}
}
4.创建一个监听器,在系统启动的时候,把所有的资源load到内存作为缓存
package com.zsw.web.loader;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.zsw.security.SecurityManager;
/**
* ------------------------------------------------------------
* 这是一个监听器
* 主要有2个方法,一个是应用启动时执行,另一个是应用关闭时执行
* 在系统启动的时候,把所有的资源load到内存作为缓存
* ------------------------------------------------------------
* 监听器的使用实际上适用于取代那些响应用户请求的Servelt,
* 所以Listener类中无须提供用户请求的方法,
* Listener的作用是为整个WEB应用提供后台服务。
* ------------------------------------------------------------
* @author 周尚武
*/
public
class ServletContextLoaderListener
implements ServletContextListener {
/**
* 应用启动时,该方法调用
* 将资源的存放在servletContext中
*/
public
void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
SecurityManager securityManager =
this.getSecurityManager(servletContext);
Map<
String,
String> urlAuthorities = securityManager.loadUrlAuthorities();
servletContext.setAttribute(
"urlAuthorities", urlAuthorities);
}
/**
* 应用关闭时该方法调用
*/
public
void contextDestroyed(ServletContextEvent servletContextEvent) {
servletContextEvent.getServletContext().removeAttribute(
"urlAuthorities");
}
protected SecurityManager getSecurityManager(ServletContext servletContext) {
return (SecurityManager) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean(
"securityManager");
}
}
5.获取当前用户
package com.zsw.security.support;
import org.springframework.security.context.SecurityContextHolder;
import com.zsw.entity.User;
/**
* Spring Security提供了一个线程安全的对象:SecurityContextHolder,
* 通过这个对象,我们可以访问当前的登录用户
* @author Administrator
*/
public
class SecurityUserHolder {
/**
* 获取当前用户
* @return
*/
public
static User getCurrentUser() {
return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
6.对资源进行认证
package com.zsw.security.interceptor;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.util.AntUrlPathMatcher;
import org.springframework.security.util.RegexUrlPathMatcher;
import org.springframework.security.util.UrlMatcher;
/**
* 资源拦截器
* 编写自己的FilterInvocationDefinitionSource实现类,对资源进行认证
*/
public
class SecureResourceFilterInvocationDefinitionSource
implements FilterInvocationDefinitionSource, InitializingBean {
private UrlMatcher urlMatcher;
private
boolean useAntPath = true;
private
boolean lowercaseComparisons = true;
public
void setUseAntPath(
boolean useAntPath) {
this.useAntPath = useAntPath;
}
public
void setLowercaseComparisons(
boolean lowercaseComparisons) {
this.lowercaseComparisons = lowercaseComparisons;
}
public
void afterPropertiesSet()
throws
Exception {
this.urlMatcher =
new RegexUrlPathMatcher();
if (useAntPath) {
this.urlMatcher =
new AntUrlPathMatcher();
}
if (
"true".equals(lowercaseComparisons)) {
if (!
this.useAntPath) {
((RegexUrlPathMatcher)
this.urlMatcher).setRequiresLowerCaseUrl(true);
}
}
else
if (
"false".equals(lowercaseComparisons)) {
if (
this.useAntPath) {
((AntUrlPathMatcher)
this.urlMatcher).setRequiresLowerCaseUrl(false);
}
}
}
public ConfigAttributeDefinition getAttributes(Object filter)
throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) filter;
String requestURI = filterInvocation.getRequestUrl();
Map<
String,
String> urlAuthorities =
this.getUrlAuthorities(filterInvocation);
String grantedAuthorities = null;
for(Iterator<Map.Entry<
String,
String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
Map.Entry<
String,
String> entry = iter.next();
String url = entry.getKey();
if(urlMatcher.pathMatchesUrl(url, requestURI)) {
grantedAuthorities = entry.getValue();
break;
}
}
if(grantedAuthorities != null) {
ConfigAttributeEditor configAttrEditor =
new ConfigAttributeEditor();
configAttrEditor.setAsText(grantedAuthorities);
return (ConfigAttributeDefinition) configAttrEditor.getValue();
}
return null;
}
@SuppressWarnings(
"unchecked")
public Collection getConfigAttributeDefinitions() {
return null;
}
@SuppressWarnings(
"unchecked")
public
boolean supports(Class clazz) {
return true;
}
@SuppressWarnings(
"unchecked")
private Map<
String,
String> getUrlAuthorities(FilterInvocation filterInvocation) {
ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
return (Map<
String,
String>)servletContext.getAttribute(
"urlAuthorities");
}
}
7.创建登陆login页面及登陆成功后的index页面:
login.jsp
<
%@ page language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<
!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html xmlns=
"http://www.w3.org/1999/xhtml"
lang=
"zh-CN"
>
<
head>
<
title>Spring Security<
/title>
<
/head>
<
body>
<
form method=
"post"
id=
"loginForm"
action=
"${pageContext.request.contextPath}/j_spring_security_check"
>
<
p>User Name:<
/p>
<
p><
input type=
"text"
name=
"j_username"
id=
"j_username"
/><
/p>
<
p>Password:<
/p>
<
p><
input type=
"password"
name=
"j_password"
id=
"j_password"
/><
/p>
<
p><
input type=
"submit"
value=
"submit"
/><
/p>
<
/form>
<
/body>
<
/html>
index.jsp
<
p>
<
a href=
"${pageContext.request.contextPath}/j_spring_security_logout"
>logout<
/a>
<
/p>
<
p>Hello ${currentUser.name}, your role is: ${currentUser.authoritiesString}<
/p>
8.相关的配置文件:
8.1web.xml
<
?xml version=
"1.0"
encoding=
"UTF-8"
?>
<
web-app id=
"Yoda"
version=
"2.4"
xmlns=
"http://java.sun.com/xml/ns/j2ee"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
<
display-name>Yoda<
/display-name>
<!--- Spring ApplicationContext Definition -->
<
context-param>
<
param-name>contextConfigLocation<
/param-name>
<
param-value>/WEB-INF/classes/applicationContext-*.xml<
/param-value>
<
/context-param>
<!---
Spring security Filter
这个Filter会拦截所有的URL请求,并且对这些URL请求进行Spring Security的验证。
-->
<
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>
<
servlet>
<
servlet-name>index<
/servlet-name>
<
servlet-class>com.zsw.web.servlet.Index<
/servlet-class>
<
/servlet>
<
servlet-mapping>
<
servlet-name>index<
/servlet-name>
<
url-pattern>/index<
/url-pattern>
<
/servlet-mapping>
<
listener>
<
listener-class>org.springframework.web.context.ContextLoaderListener<
/listener-class>
<
/listener>
<
listener>
<
listener-class>com.zsw.web.loader.ServletContextLoaderListener<
/listener-class>
<
/listener>
<
/web-app>
8.2applicationContext-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"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
">
<
beans:bean id=
"loggerListener"
class=
"org.springframework.security.event.authentication.LoggerListener"
/>
<
http access-denied-page=
"/403.jsp"
>
<
intercept-url pattern=
"/static/**"
filters=
"none"
/>
<
intercept-url pattern=
"/template/**"
filters=
"none"
/>
<
intercept-url pattern=
"/"
filters=
"none"
/>
<
intercept-url pattern=
"/login.jsp"
filters=
"none"
/>
<!--- 登录认证 -->
<
form-login login-page=
"/login.jsp"
authentication-failure-url=
"/login.jsp?error=true"
default-target-url=
"/index"
/>
<!--- 注销处理 -->
<
logout logout-success-url=
"/login.jsp"
/>
<!--- 基本认证 -->
<
http-basic />
<
/http>
<
authentication-manager alias=
"authenticationManager"
/>
<!--- 配置UserDetailsService来指定用户和权限 -->
<
authentication-provider user-service-ref=
"securityManager"
>
<
password-encoder hash=
"md5"
/>
<
/authentication-provider>
<
beans:bean id=
"accessDecisionManager"
class=
"org.springframework.security.vote.AffirmativeBased"
>
<
beans:property name=
"allowIfAllAbstainDecisions"
value=
"false"
/>
<
beans:property name=
"decisionVoters"
>
<
beans:list>
<
beans:bean class=
"org.springframework.security.vote.RoleVoter"
/>
<
beans:bean class=
"org.springframework.security.vote.AuthenticatedVoter"
/>
<
/beans:list>
<
/beans:property>
<
/beans:bean>
<!---
由于我们所实现的是FilterSecurityInterceptor中的一个开放接口,
所以我们实际上定义了一个新的bean,
并通过<custom-filter after="LAST" />插入到过滤器链中去。
-->
<
beans:bean id=
"resourceSecurityInterceptor"
class=
"org.springframework.security.intercept.web.FilterSecurityInterceptor"
>
<
beans:property name=
"authenticationManager"
ref=
"authenticationManager"
/>
<
beans:property name=
"accessDecisionManager"
ref=
"accessDecisionManager"
/>
<
beans:property name=
"objectDefinitionSource"
ref=
"secureResourceFilterInvocationDefinitionSource"
/>
<
beans:property name=
"observeOncePerRequest"
value=
"false"
/>
<
custom-filter after=
"LAST"
/>
<
/beans:bean>
<
beans:bean id=
"secureResourceFilterInvocationDefinitionSource"
class=
"com.zsw.security.interceptor.SecureResourceFilterInvocationDefinitionSource"
/>
<
/beans:beans>
9. 以上Demo下载地址:
http://download.csdn.net/source/3500432