Spring Security简单Demo

来源:互联网 发布:电子图书馆软件的功能 编辑:程序博客网 时间:2024/06/11 15:46

 

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<StringString> 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<StringString> loadUrlAuthorities() {
        Map<StringString> urlAuthorities = new HashMap<StringString>();
        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<StringString> 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<StringString> urlAuthorities = this.getUrlAuthorities(filterInvocation);        
        String grantedAuthorities = null;
        for(Iterator<Map.Entry<StringString>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
            Map.Entry<StringString> 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<StringString> getUrlAuthorities(FilterInvocation filterInvocation) {
        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
        return (Map<StringString>)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

 

原创粉丝点击