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

这里在一个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“的权限名。才能访问”/*“下的资源。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值