`
ll_feng
  • 浏览: 389315 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

springsecurity学习笔之二:实现一个基于数据库的简单权限系统

    博客分类:
  • j2ee
阅读更多
这里在一个web工程中,通过三张表,实现用户、角色、权限的关系实现一个相对简单的权限系统。没有考虑对资源(URL)的控制
一、在web工程中加入springsecurity的支持,主要jar包

二、配置web容器:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


	<!--
		- Location of the XML file that defines the root application context -
		Applied by ContextLoaderListener.
	-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:applicationContext.xml
            classpath:applicationContext-sec.xml
            <!-- classpath:applicationContext-common-business.xml
            classpath:applicationContext-common-authorization.xml
            classpath:applicationContext-security.xml -->
		</param-value>
	</context-param>


	<filter>
		<filter-name>localizationFilter</filter-name>
		<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
	</filter>

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>


	<filter-mapping>
		<filter-name>localizationFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>
			org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!--
		- Loads the root application context of this web app at startup. - The
		application context is then available via -
		WebApplicationContextUtils.getWebApplicationContext(servletContext).
	-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<error-page></error-page>

	<session-config>
		<session-timeout>20</session-timeout>
	</session-config>

	<!-- 出错页面定义 -->
	<error-page>
		<exception-type>java.lang.Throwable</exception-type>
		<location>/commons/error.jsp</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/commons/500.jsp</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/commons/404.jsp</location>
	</error-page>
	<error-page>
		<error-code>403</error-code>
		<location>/commons/403.jsp</location>
	</error-page>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>



三、配置spring : applicationContext-springsecurity.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="  
       http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans.xsd  
       http://www.springframework.org/schema/security  
       http://www.springframework.org/schema/security/spring-security-3.0.xsd">
	<http auto-config="true">
		<form-login login-page="/login.jsp"/>
		<intercept-url pattern="/login.jsp*" filters="none"/>
		<intercept-url pattern="/*" access="ROLE_USER" /><!-- 配置资源的权限的关系 -->
		<!-- <http-basic /> -->
	</http>
	<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="userDetailService">
			<!-- 
			<user-service>
				<user authorities="ROLE_USER" name="guest" password="guest" />
			</user-service>
			 -->
			 <password-encoder hash="plaintext"></password-encoder>
			 
		</authentication-provider>
	</authentication-manager>
	
	<beans:bean id="userDetailService" class="com.harmony.cap.auth.service.UserDetailsServiceImpl"/>
</beans:beans>


四、增加自己定义的登录页面:login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
  </head>
  
  <body>
    This is my JSP page. <br>
    <form name='f' action='${pageContext.request.contextPath }/j_spring_security_check'  method='POST'>

  User:<input type='text' name='j_username' value=''>

  Password:<input type='password' name='j_password'/>

  <input name="submit" type="submit"/>

  <input name="reset" type="reset"/>

</form>
  </body>
</html>



五、增加权限类:User.java、Role.java、Privileage.java
1、User.java
package cn.ibeans.ssh.model;

import java.sql.Blob;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

import cn.ibeans.common.utils.reflection.ConvertUtils;

/**
 * 用户.
 * 
 * 使用JPA annotation定义ORM关系.
 * 使用Hibernate annotation定义JPA 1.0未覆盖的部分.
 * 
 * @author calvin
 */
@Entity
//表名与类名不相同时重新定义表名.
@Table(name = "SYS_USERS")
//默认的缓存策略.
//@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User extends IdEntity {

	private String loginId;
	private String password;//为简化演示使用明文保存的密码
	private String name;
	private String email;
	private String resume;
	private Blob photo;
	private List<Role> roleList = new ArrayList();//有序的关联对象集合

	//字段非空且唯一, 用于提醒Entity使用者及生成DDL.
	@Column(nullable = false, unique = true)
	public String getLoginId() {
		return loginId;
	}
	
	public void setLoginId(String loginId) {
		this.loginId = loginId;
	}

	@Column(name="password")
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	@Column(name="resume")
	public String getResume() {
		return resume;
	}

	public void setResume(String resume) {
		this.resume = resume;
	}

	@Column(name="photo")
	public Blob getPhoto() {
		return photo;
	}

	public void setPhoto(Blob photo) {
		this.photo = photo;
	}

	@Column(name="name")
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Column(name="email")
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	//多对多定义
	@ManyToMany
	//中间表定义,表名采用默认命名规则
	@JoinTable(name = "SYS_USER_ROLE", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
	//Fecth策略定义
	@Fetch(FetchMode.SUBSELECT)
	//集合按id排序.
	@OrderBy("id")
	//集合中对象id的缓存.
	//@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
	public List<Role> getRoleList() {
		return roleList;
	}

	public void setRoleList(List<Role> roleList) {
		this.roleList = roleList;
	}

	/**
	 * 用户拥有的角色名称字符串, 多个角色名称用','分隔.
	 */
	//非持久化属性.
	@Transient
	public String getRoleNames() {
		return "";//ConvertUtils.convertElementPropertyToString(roleList, "name", ", ");
	}

	/**
	 * 用户拥有的角色id字符串, 多个角色id用','分隔.
	 */
	//非持久化属性.
	@Transient
	@SuppressWarnings("unchecked")
	public List<Long> getRoleIds() {
		return null;//ConvertUtils.convertElementPropertyToList(roleList, "id");
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}
}


2、Role.java
package cn.ibeans.ssh.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;


/**
 * 角色.
 * 
 * 注释见{@link User}.
 * 
 * @author calvin
 */
@Entity
@Table(name = "SYS_ROLES")
//@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Role extends IdEntity {

	private String name;
	private List<Privilege> privilegeList = new ArrayList();//Lists.newArrayList();

	public Role() {

	}

	public Role(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	@Column(nullable = false, unique = true)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@ManyToMany
	@JoinTable(name = "SYS_ROLE_PRIVILEGE", joinColumns = { @JoinColumn(name = "ROLE_ID") }, inverseJoinColumns = { @JoinColumn(name = "PRIVILEGE_ID") })
	@Fetch(FetchMode.SUBSELECT)
	@OrderBy("id")
	//@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
	public List<Privilege> getPrivilegeList() {
		return privilegeList;
	}

	public void setPrivilegeList(List<Privilege> privilegeList) {
		this.privilegeList = privilegeList;
	}

	@Transient
	public String getAuthNames() {
		return "";//ConvertUtils.convertElementPropertyToString(authorityList, "name", ", ");
	}

	@Transient
	@SuppressWarnings("unchecked")
	public List<Long> getAuthIds() {
		return null;//ConvertUtils.convertElementPropertyToList(authorityList, "id");
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}
}



3、Privilege.java
package cn.ibeans.ssh.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;


/**
 * 权限.
 * 
 * 注释见{@link User}.
 * 
 * @author calvin
 */
@Entity
@Table(name = "SYS_PRIVILEGE")
//@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Privilege extends IdEntity {

	/**
	 * SpringSecurity中默认的角色/授权名前缀.
	 */
	public static final String AUTHORITY_PREFIX = "ROLE_";

	private String name;

	public Privilege() {
	}

	public Privilege(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	@Column(nullable = false, unique = true)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Transient
	public String getPrefixedName() {
		return AUTHORITY_PREFIX + name;
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}
}


以上三个类,实际上还需要两个中间表。为了方便我采用系统自动建表。即:
<prop key="hibernate.hbm2ddl.auto">update</prop>


六、实现自定义的用户管理类:UserDetailsServiceImpl.java
package cn.ibeans.ssh.auth.service;

import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;

import cn.ibeans.ssh.auth.model.Privilege;
import cn.ibeans.ssh.auth.model.Role;
import cn.ibeans.ssh.auth.model.User;

/**
 * 实现SpringSecurity的UserDetailsService接口,实现获取用户Detail信息的回调函数.
 * 
 * @author calvin
 */
@Transactional(readOnly = true)
public class UserDetailsServiceImpl implements UserDetailsService {

	Logger logger = Logger.getLogger(this.getClass());
	private AccountManager accountManager;

	/**
	 * 获取用户Details信息的回调函数.
	 */
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
		User user = accountManager.findUserByLoginName(username);
		if (user == null) {
			throw new UsernameNotFoundException("用户" + username + " 不存在");
		}

		Set<GrantedAuthority> grantedAuths = obtainGrantedAuthorities(user);

		//-- mini-web示例中无以下属性, 暂时全部设为true. --//
		boolean enabled = true;
		boolean accountNonExpired = true;
		boolean credentialsNonExpired = true;
		boolean accountNonLocked = true;

		UserDetails userdetails = new org.springframework.security.core.userdetails.User(user.getLoginId(), user
				.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
		logger.debug("用户详细信息已获得。"+userdetails.getUsername()+"/"+userdetails.getPassword());
		return userdetails;
	}

	/**
	 * 获得用户所有角色的权限集合.
	 */
	private Set<GrantedAuthority> obtainGrantedAuthorities(User user) {
		Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
		for (Role role : user.getRoleList()) {
			for (Privilege privilege : role.getPrivilegeList()) {
				authSet.add(new GrantedAuthorityImpl(privilege.getPrefixedName()));
			}
		}
		return authSet;
	}

	@Autowired
	public void setAccountManager(AccountManager accountManager) {
		this.accountManager = accountManager;
	}
}



七、初化数据
为了能让系统能快速运行起来,可直接在数据库中插入基本数据:
分别user、role、privilege表及其中间表中插入测试数据,建立起它们之间的关系。系统基本可以运行了。


八、小结
1、web.xml里的配置要把springsecurity的拦截配置在前面,否则会被struts2首先拦截并报找不到action的错误。
2、自定义登录页面中的url和用户名、密码属性的定义不能随意。springsecurity自带的过虑器(UsernamePasswordAuthenticationFilter)中有定义:
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
    public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;

    //~ Constructors ===================================================================================================

    public UsernamePasswordAuthenticationFilter() {
        super("/j_spring_security_check");
    }


3、权限表(privilege)的数据必须有与springsecurity配置中的权限一致的值,用户才能获得授权,否则无法通过。
比如:
<http auto-config="true">
		<form-login login-page="/login.jsp"/>
		<intercept-url pattern="/login.jsp*" filters="none"/>
		<intercept-url pattern="/*" access="ROLE_USER" /><!-- 配置资源的权限的关系 -->
		<!-- <http-basic /> -->
	</http>

那么,privilege表中,要有一项为“USER“的权限名。才能访问”/*“下的资源。
分享到:
评论

相关推荐

    基于Springboot+Vue的药店管理系统的设计与实现源码案例设计带文档说明.zip

    《基于Springboot+Vue的药店管理系统的设计与实现》 在当今信息化时代,药店管理系统已经成为药店日常运营不可或缺的一部分。本项目采用先进的技术栈Springboot和Vue,构建了一个高效、易用的药店管理系统,旨在...

    java web实战:基于SSM框架实现的超市账单管理系统.zip

    本项目“java web实战:基于SSM框架实现的超市账单管理系统”是一个典型的后端项目,适用于毕业设计、课程设计或自我提升的学习实践。 1. **Spring框架**:Spring是Java企业级应用的核心框架,提供依赖注入(DI)和...

    基于 JAVA的医药管理系统设计(LW+源代码).zip

    总结,基于JAVA的医药管理系统设计是一个集成了JAVA技术、数据库管理、软件工程等多个领域的综合项目。理解并掌握这些知识点,对于提升医药行业信息化水平具有重要意义。通过实际的源代码学习,开发者不仅可以深化对...

    基于SSM的加油站系统 和微信小程序源码.zip

    标题中的“基于SSM的加油站系统 和微信小程序源码”指的是一个使用了Spring、SpringMVC和MyBatis(简称SSM)框架开发的加油站管理系统,并且配套有微信小程序的前端源代码。SSM是Java web开发中常用的一个集成框架,...

    基于SpringBoot+Vue的外卖网上点餐系统.zip

    本系统“基于SpringBoot+Vue的外卖网上点餐系统”就是一个很好的实例,它整合了后端强大的SpringBoot框架与前端流行的Vue.js库,为用户提供了一个流畅的点餐体验。下面我们将详细探讨该系统的核心技术和实现细节。 ...

    java ssm汽车维修管理系统源码 4s店管理系统源码.rar

    Java SSM汽车维修管理系统源码是一款专为4S店设计的管理软件,它采用Java技术栈,基于Spring、SpringMVC和MyBatis(SSM)三大主流框架进行开发。这款系统提供了完整的功能模块,旨在优化汽车维修业务流程,提高4S店...

    基于ssm削面快餐店点餐服务系统.zip

    "基于ssm削面快餐店点餐服务系统"是一个使用SSM(Spring、SpringMVC、MyBatis)技术栈开发的项目,主要服务于削面快餐店的点餐服务。SSM是Java后端开发中常用的一个框架组合,用于构建高效、可扩展的企业级应用。 ...

    基于ssm微信小程序的家庭记账本源码数据库文档.zip

    这是一个基于SSM(Spring、SpringMVC、MyBatis)框架和微信小程序开发的家庭记账本项目的源码和数据库文档。这个项目对于学习SSM框架的实际应用和微信小程序开发具有很高的参考价值,同时也适合作为毕业设计的示例。...

    003 基于ssm+extjs整合的个人记账管理系统

    SSM(Spring、SpringMVC、MyBatis)框架整合是Java Web开发中常见的技术栈,主要用于构建高效、模块化的...通过以上知识点的学习和实践,开发者可以构建出一个稳定、易用的个人记账管理系统,满足用户的财务管理需求。

    基于ssm+vue的KTV包厢管理系统.zip

    本系统——基于SSM+Vue的KTV包厢管理系统,是集成了Spring、SpringMVC、MyBatis(SSM)三大框架和前端Vue.js技术的一体化解决方案,旨在提供一个全面、智能且用户友好的管理平台。 首先,我们来深入理解SSM框架。...

    java Spring团购完整前后台进阶

    同时,Spring Security可以用于团购系统的权限控制,保护用户信息和交易安全。 本教程的视频内容可能涵盖以上各个方面,包括Spring环境搭建、MVC结构设计、数据库交互、事务管理、安全控制等,并可能提供实际的团购...

    图书销售管理系统设计与实现

    总的来说,《图书销售管理系统设计与实现》项目是一个结合了后端开发、前端设计和数据库管理的综合实践,对于学习和掌握Java Web开发技术具有很高的参考价值。通过这个系统,开发者不仅可以提升SSM框架的使用技巧,...

    JSP基于SSM个人记账本理财管理系统源码案例设计.zip

    本篇将详细介绍一个基于Java Web的SSM框架(Spring、SpringMVC、MyBatis)实现的JSP个人记账本理财管理系统。通过源码分析,我们可以深入理解系统的设计思想和实现机制。 1. **项目结构** 该项目的源码结构通常...

    Java SSM 超市订单管理系统【优质毕业设计分享】

    7. **项目结构**:一个典型的SSM项目结构包括src/main/java(存放源代码)、src/main/resources(配置文件及资源文件)、src/main/webapp(Web应用目录,含静态资源和Web-INF)等。 8. **测试与部署**:项目完成后...

    Struts版的汽车租凭系统源码

    Struts版的汽车租赁系统源码是一个基于Java Web开发的项目,主要利用了Apache Struts框架来构建。Struts是一个开源的MVC(Model-View-Controller)框架,它为开发者提供了一种组织应用程序结构的方式,使代码更易于...

    通讯录系统

    例如,使用Spring Security框架可以对用户登录进行控制,限制不同用户对通讯录数据的访问权限。 在系统架构上,通讯录系统可能遵循MVC(Model-View-Controller)模式,将业务逻辑、数据模型和用户界面分离,使得...

    新奥家电连锁网络系统.zip-Java源码项目

    新奥家电连锁网络系统是一个基于Java编程语言的软件项目,主要目标是实现家电连锁店的信息化管理。这个项目可能包含了诸如库存管理、销售记录、客户关系管理、订单处理、供应链优化等多个模块,旨在提高家电连锁企业...

    Spring-study-master.zip

    学习资源中可能会涉及如何配置Spring Security,实现用户登录、权限控制等功能。 总的来说,"Spring-study-master.zip"是一份全面且深入的Spring学习资源,涵盖了Spring的核心特性和常见应用场景。配合狂神老师的...

    淘淘商城笔记

    由于文件名仅给出"笔商城记",推测这可能是文档的部分章节或者子目录,具体内容可能包括但不限于系统设计、后端开发、前端实现、数据库管理、性能优化等多个方面。下面我们将根据Java在电商领域的常见应用来展开讨论...

    Java项目之网络考试系统

    7. 用户界面:一个友好的用户界面是吸引用户的关键。可以使用Java的Web框架如Spring Boot结合前端技术如HTML、CSS和JavaScript(可能搭配React或Vue.js库)来构建响应式和互动性强的界面。 8. 数据分析:系统应具备...

Global site tag (gtag.js) - Google Analytics