Maven搭建Spring Security3.2項目詳解

前言

        本來是打算在上一篇SpringMVC+Hibernate上寫的,結果發現上面那篇一起整合的,結果發現上一篇內容實在是太長了,就另起一篇,這篇主要是採用 Maven搭建Spring+SpringMVC+Hibernate+Security整合,而Spring+SpringMVC+Hibernate已經在上一篇介紹了,在這篇將不再重複寫了,主要說明一下SpringSecurity3.2權限控制整合搭建,以及配置,使用注意事項等。

        SpringSecurity的Api文檔地址:查看

1、Maven映入SpringSecurity依賴包

      在pom.xml中引入我們需要引入spring-security-core,spring-security-config,spring-security-taglibs三個包,如下

    

               <!-- spring-security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${security.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${security.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>${security.version}</version>
		</dependency>

2、配置security的配置文件

      我們新建一個配置文件(起名隨意),我這兒就叫spring-security.xml,我現在定義了幾個權限,權限信息表內容如下:

    

  其中:管理用戶和全部用戶的權限將通過jsp中security標籤配置,其他的通過spring-security.xml文件配置。

  先貼出spring-security.xml的文件,詳細配置含義我將做一個簡單的說明:

    

<span style="font-size:14px;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:security="http://www.springframework.org/schema/security"
	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-4.1.xsd
                        http://www.springframework.org/schema/security 
                        http://www.springframework.org/schema/security/spring-security-3.2.xsd"
	default-lazy-init="true">

	<description>spring-security配置</description>

	<!-- 靜態資源 -->
	<security:http pattern="/css/**" security="none" />
	<security:http pattern="/js/**" security="none" />
	<security:http pattern="/images/**" security="none" />

	<security:http>
		<security:intercept-url pattern="/user/save*"
			access="ROLE_添加用戶" requires-channel="any" />
		<security:intercept-url pattern="/user/delete*"
			access="ROLE_刪除用戶" requires-channel="any" />
		<security:intercept-url pattern="/user/user*"
			access="ROLE_瀏覽用戶" requires-channel="any" />
		<security:intercept-url pattern="/user/update*"
			access="ROLE_修改用戶" requires-channel="any" />

		<security:session-management>
			<security:concurrency-control
				expired-url="/login/login.htmls?repeat=true" max-sessions="1"
				error-if-maximum-exceeded="true" />
		</security:session-management>

		<security:form-login login-page="/login/login.htmls"
			authentication-failure-url="/login/login.htmls?error=true"
			default-target-url="/user/main.htmls" always-use-default-target='true'
			username-parameter="nickName" password-parameter="nickPassword" />

		<security:logout invalidate-session="true"
			logout-success-url="/login/login.htmls?logout=true" />

	</security:http>

	<!-- 認證配置 自定義認證實現UserDetailsService接口 -->
	<security:authentication-manager>
		<security:authentication-provider
			user-service-ref="userDetailsService">
			<!-- 配置密碼加密方式 -->
			<security:password-encoder hash="md5" />
		</security:authentication-provider>
	</security:authentication-manager>

	<bean id="userDetailsService" class="org.andy.work.service.impl.UserDetailsServiceImpl" />
	
</beans></span>

  其中:我們配置了靜態文件管理,session對話管理,登錄管理,註銷配置,權限配置,自定義數據表權限認證配置。

2.1、靜態文件管理

    我們對於css,image,js這些不用權限攔截。

2.2、session對話管理

   session管理max-sessions="1"配置了最多有一個用戶登錄,(在wab.xml還要添加如下:)

         

<span style="font-size:14px;"><!-- spring-security 管理session配置 -->
	<listener>
		<listener-class>
			org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	</listener></span>

    error-if-maximum-exceeded="true"  值爲true時,若果有一個用戶登錄,第二個用戶無法登陸,

                                                                  值爲false時,若果有一個用戶已經登錄,下一個用戶登錄將踢掉上一個用戶。

    自然在session-manager中我們可以配置session失效時,跳轉的url如,invalid-session-url="/invalidSession.jsp",如果session失效時,刷新將跳轉到invalidSession.jsp頁面。

2.3、登錄管理

    通過security:form-login配置登錄,login-page爲登錄跳轉的url,authentication-failure-url爲登錄失敗時的url(次url可以不存在),

   username-parameter和password-parameter爲登錄表單是用戶名和密碼,如果不寫默認爲j_password和j_username

   login-processing-url:爲登錄時表單action跳轉的url,如果不填寫默認爲 j_spring_security_check。

2.4、註銷管理

   通過security:logout標籤配置註銷,invalidate-session:註銷時session是否失效。logout-success-url:註銷成功後跳轉的地址。

logout-url:爲註銷的url,如果不填寫,則默認爲j_spring_security_logout

2.5、權限配置

   security:intercept-url爲要攔截權限認證的的url,pattern爲攔截的正則匹配url,access:所需的權限,可以是一個權限組,用逗號隔開,requires-channel:攔截http還是https的,如果兩個都攔截用any。

2.6、配置自定義的權限認證機制

    通過我們數據庫的權限表信息,我們自定義權限認證機制,需要我們實現UserDetailsService接口,配置用戶密碼的加密方式。

security:password-encoder:配置密碼加密規則。

3、web.xml容器配置

   

<span style="font-size:14px;">         <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:spring.xml
		    classpath:spring-hibernate.xml
			classpath:spring-security.xml
		</param-value>
	</context-param>

       <!-- Spring-Security filter 最好配置在控制層filter的前面 -->
	<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-security 管理session配置 -->
	<listener>
		<listener-class>
			org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	</listener></span>
 

配置了加載的spring-security文件,security攔截的filter,以及security的session監聽。

4、自定義認證,實現UserDetailsService接口

UserDetailsServiceImpl如下:

<span style="font-size:14px;">package org.andy.work.service.impl;

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

import org.andy.work.dao.UserDao;
import org.andy.work.entity.AcctAuthority;
import org.andy.work.entity.AcctRole;
import org.andy.work.entity.AcctUser;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;

/**
 * 創建時間:2015-2-9 下午5:24:44
 * 
 * @author andy
 * @version 2.2
 *          <p>
 *          描述: 實現SpringSecurity的UserDetails接口 自定義認證
 */

public class UserDetailsServiceImpl implements UserDetailsService {

	private static final Logger LOGGER = Logger
			.getLogger(UserDetailsServiceImpl.class);

	// 注入查詢User的dao層
	@Autowired
	private UserDao userDao;

	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		LOGGER.info("認證用戶:" + username);

		// 查詢數據庫獲取改用戶的信息
		AcctUser acctUser = userDao.findByNickName(username);

		if (null == acctUser) {
			throw new UsernameNotFoundException("用戶:" + username + "不存在");
		}

		Set<GrantedAuthority> authorities = getAuthorities(acctUser);

		// 將沒有使用到的屬性設置爲true
		UserDetails userDetails = new User(acctUser.getNickName(),
				acctUser.getNickPassword(), true, true, true, true, authorities);

		return userDetails;
	}

	// 獲得用戶所有角色的權限
	private Set<GrantedAuthority> getAuthorities(AcctUser acctUser) {
		Set<GrantedAuthority> authoritySet = new HashSet<GrantedAuthority>();

		// 默認所有的用戶有"瀏覽用戶"的權利
		authoritySet.add(new SimpleGrantedAuthority("ROLE_瀏覽用戶"));

		// 依次添加
		if (null != acctUser.getAcctRoles()
				&& acctUser.getAcctRoles().size() > 0)
			for (AcctRole role : acctUser.getAcctRoles()) {
				if (null != role.getAcctAuthorities()
						&& role.getAcctAuthorities().size() > 0)
					for (AcctAuthority authority : role.getAcctAuthorities()) {
						authoritySet.add(new SimpleGrantedAuthority(authority
								.getPrefixedName()));
					}
			}

		return authoritySet;
	}

}
</span>

涉及到的AcctUser,AcctRole,AcctAuthority類我就不貼了,不然一大把,我會提供源碼。

5、Security的認證錯誤提示

    我們可以通過sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message獲取認證錯誤。

6、Security獲取session中的用戶名和用戶信息

     security將用戶信息存放在session中,可以通過以下兩種獲得:

6.1、後臺獲取

    

<span style="font-size:14px;">		//獲取security的上下文
		SecurityContext securityContext = SecurityContextHolder.getContext();
		//獲取認證對象
		Authentication authentication = securityContext.getAuthentication();
		//在認證對象中獲取主體對象
		Object principal = authentication.getPrincipal();
		
		String username = "";
		if(principal instanceof UserDetails){
			username = ((UserDetails) principal).getUsername();
		}else {
			username = principal.toString();
		}</span>

6.1、前臺獲取

    先引入security的標籤庫:

<span style="font-size:14px;"><%@taglib prefix="security" uri="http://www.springframework.org/security/tags"%></span>

在查看:
<span style="font-size:14px;"><security:authentication property="name"/></span>

7、前臺標籤授權

    通過security:authorize標籤設置權限,其有三種屬性分別如下:

        ifAnyGranted::只有當前用戶擁有所指定的權限中的一個的時候,就能顯示標籤內部的內容(相當於“或”的關係)

          ifAllGranted:只有當前用戶擁有所指定的權限時,才顯示標籤的內容(相當於“與”的關係)

          ifNotGranted:  沒有指定的權限的時候,顯示標籤內容(相當於“非”的關係

自然也可以通過method限制是那種http的請求(http的請求有8種:- GET - DELETE - HEAD - OPTIONS    - POST     - PUT    - PATCH    - TRACE)。

8、後臺Controller

LoginController

<span style="font-size:14px;"><span style="font-size:14px;">package org.andy.work.controller;

import org.andy.work.entity.AcctUser;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 創建時間:2015-2-10 下午9:23:34
 * 
 * @author andy
 * @version 2.2 描述:
 */
@Controller
@RequestMapping("/login")
public class LoginController {

	private static final Logger LOGGER = Logger
			.getLogger(LoginController.class);

	@RequestMapping("/login")
	public String login(@ModelAttribute AcctUser acctUser,
			@RequestParam(required = false) Boolean logout,
			Errors errors
			) {
		LOGGER.info("login");
		
		if(null != logout){
			errors.reject("msg", "已經安全退出");
		}
		
		return "/login/login";
	}


}</span>
</span>

UserController類 
<span style="font-size:14px;">package org.andy.work.controller;

import java.util.List;

import org.andy.work.entity.AcctUser;
import org.andy.work.service.UserService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**  
 * 創建時間:2015-2-7 上午11:49:00  
 * @author andy  
 * @version 2.2  
 * 描述: 用戶Controller
 */
@Controller
@RequestMapping("/user")
public class UserController {

	private static final Logger LOGGER = Logger.getLogger(UserController.class);
	
	@Autowired
	private UserService userService;
	
	@RequestMapping("/showInfo/{userId}")
	public String showUserInfo(ModelMap modelMap, @PathVariable String userId){
		LOGGER.info("查詢用戶:" + userId);
		AcctUser userInfo = userService.load(userId);
		modelMap.addAttribute("userInfo", userInfo);
		return "/user/showInfo";
	}
	
	@RequestMapping("/showInfos")
	public @ResponseBody List<AcctUser> showUserInfos(){
		LOGGER.info("查詢用戶全部用戶");
		List<AcctUser> userInfos = userService.findAll();
		return userInfos;
	}
	
	
	@RequestMapping("/main")
	public String main(ModelMap modelMap){
		LOGGER.info("顯示主頁面");
		//後臺獲取security保存的session中的用戶信息
		
		//獲取security的上下文
		SecurityContext securityContext = SecurityContextHolder.getContext();
		//獲取認證對象
		Authentication authentication = securityContext.getAuthentication();
		//在認證對象中獲取主體對象
		Object principal = authentication.getPrincipal();
		
		String username = "";
		if(principal instanceof UserDetails){
			username = ((UserDetails) principal).getUsername();
		}else {
			username = principal.toString();
		}
		modelMap.addAttribute("username", username);
		return "/user/main";
	}
	
	
	@RequestMapping("/manage")
	public String manage(ModelMap modelMap){
		LOGGER.info("顯示主頁面");
		modelMap.addAttribute("msg", "manage");
		return "/user/option";
	}
	
	@RequestMapping("/save")
	public String save(ModelMap modelMap){
		LOGGER.info("保存");
		modelMap.addAttribute("msg", "save");
		return "/user/option";
	}
	
	@RequestMapping("/update")
	public String update(ModelMap modelMap){
		LOGGER.info("修改");
		modelMap.addAttribute("msg", "update");
		return "/user/option";
	}
	
	@RequestMapping("/delete")
	public String delete(ModelMap modelMap){
		LOGGER.info("刪除");
		modelMap.addAttribute("msg", "delete");
		return "/user/option";
	}
	
}
</span>

 9、前臺

login.jsp


<span style="font-size:14px;"><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>login</title>
</head>
<body>
	<div>${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}</div>

	<form:form action="j_spring_security_check" modelAttribute="acctUser" method="post">
		<form:errors path="*" cssStyle="color:red;" />
		<br />
		用戶:<form:input path="nickName" />
		<br />
		密碼:<form:password path="nickPassword" />
		<br />
		<form:button>登錄</form:button>
	</form:form>
</body>
</html></span>

main.jsp
<span style="font-size:14px;"><%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@taglib prefix="security" uri="http://www.springframework.org/security/tags"%>

<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>login</title>
</head>
<body>

    welcome! <security:authentication property="name"/>
    <br/>
    	  後臺獲取用戶名:${username }
	<div style="border: 1px; "><a target="_blank" href="j_spring_security_logout">註銷</a></div>
	
	<br/>
	
	<security:authorize ifAnyGranted="ROLE_瀏覽用戶">
	<div>
		<a target="_blank" href="user/showInfo.htmls">全部用戶</a>
	</div>
	</security:authorize>
	
	
	<security:authorize ifAllGranted="ROLE_管理用戶">
		<div>
			<a target="_blank" href="user/manage.htmls">管理用戶</a>
		</div>
	</security:authorize>
	
	<div>
		<a target="_blank" href="user/save.htmls">添加用戶</a>
	</div>
	<div>
		<a target="_blank" href="user/update.htmls">修改部用戶</a>
	</div>
	<div>
		<a target="_blank" href="user/delete.htmls">刪除用戶</a>
	</div>
	
</body>
</html></span>

10、測試

    現在andy用戶擁有“瀏覽用戶”和“添加用戶”權限

用戶密碼錯誤時:

    

  正確密碼登陸後:

     

    

點擊添加用戶,正確跳轉。如下:




點擊全部用戶,無權限提示。

11、Spring security自定義認證錯誤提示

    首先,拷貝spring-security-core寫的messages_zh_CN.properties國際化文件,放到項目的src/main/resources目錄中

          修改對應的提示,按照我們自己的需求,我命名爲messages.properties

   其次, 我們需在spring的配置文件中添加如下內容:

<span style="font-size:14px;"><span style="font-size:14px;">	<!-- 定義上下文返回的消息的國際化 -->
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<property name="basename" value="classpath:messages" />
	</bean>

	<bean id="localeResolver"
		class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" /></span></span>

ok,Maven搭建Spring+Hibernate+security整合完畢。


後續

遺留問題,通用的重寫security提示信息,其他的都能國際化漢語提示,但是

唯獨

ConcurrentSessionControlStrategy.exceededAllowed=\u5DF2\u7ECF\u6709 {0} \u4E2A\u7528\u6237\u767B\u5F55\uFF0C\u4E0D\u80FD\u91CD\u590D\u767B\u5F55

我修改重複登錄提示時,還是security原來自帶的提示,如下:

     


希望又遇到的共同留言探討。


博客地址http://blog.csdn.net/fengshizty

源碼地址http://download.csdn.net/detail/fengshizty/8444061



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章