鍍金池/ 教程/ Java/ Spring Security基于角色登錄實(shí)例
Spring Security自定義表單登錄注釋示例
Spring Secrity與Hibernate基于角色登錄實(shí)例
Spring Security自定義表單登錄實(shí)例
Secure Spring REST API使用OAuth2
Spring Security與Hibernate整合以及XML實(shí)例
Spring Security入門程序示例
Spring Security+Hibernate密碼編碼器Bcrypt實(shí)例
Spring Security標(biāo)簽庫顯示視圖
Spring Security基于角色登錄實(shí)例
Spring Security使用@PreAuthorize,@PostAuthorize, @Secured方法安全
Secure Spring REST API使用基本認(rèn)證
Spring Security入門程序注釋示例
Spring Security+Hibernate記住我實(shí)例
Spring MVC4 + Spring Security4 + Hibernate實(shí)例
Spring Security注銷登錄實(shí)例
AngularJS+Spring Security使用基本身份驗(yàn)證
Spring Security教程

Spring Security基于角色登錄實(shí)例

本教程介紹了Spring Security的基于角色登錄。這意味著程序可根據(jù)自己分配的角色在登錄時將用戶重定向到不同的URL。

最基本的,我們要做的是創(chuàng)建一個自定義的成功處理程序來負(fù)責(zé)根據(jù)登錄用戶的角色重定向到相應(yīng)的URL。Spring Security中已經(jīng)提供了 SimpleUrlAuthenticationSuccessHandler,它包含了成功處理程序的邏輯。我們只是用自己的重定向邏輯擴(kuò)展它來實(shí)現(xiàn)我們的目標(biāo)。

當(dāng)這個處理程序成功,我們將通過注冊 formLogin() 或 loginPage()。完整的例子如下所示。

需要用到的技術(shù),如下所示 - 
  • Spring 4.1.6.RELEASE
  • Spring Security 4.0.1.RELEASE
  • Maven 3
  • JDK 1.8
  • Tomcat 7
  • Eclipse JUNO Service Release 2

讓我們現(xiàn)在就開始,建議您按照以下步驟一起來實(shí)踐。

步驟1: 工程目錄結(jié)構(gòu)

現(xiàn)在,讓我們解釋在上面添加的結(jié)構(gòu)每個細(xì)節(jié)提及的內(nèi)容。

第2步:更新pom.xml,包括所需依懶

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yiibai.springsecurity</groupId>
  <artifactId>SpringSecurityRoleBasedLogin</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <name>SpringSecurityRoleBasedLogin</name>

	<properties>
		<springframework.version>4.1.6.RELEASE</springframework.version>
		<springsecurity.version>4.0.1.RELEASE</springsecurity.version>
	</properties>

	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${springframework.version}</version>
		</dependency>

		<!-- Spring Security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${springsecurity.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${springsecurity.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<source>1.7</source>
						<target>1.7</target>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<version>2.4</version>
					<configuration>
						<warSourceDirectory>src/main/webapp</warSourceDirectory>
						<warName>SpringSecurityRoleBasedLoginExample</warName>
						<failOnMissingWebXml>false</failOnMissingWebXml>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
		<finalName>SpringSecurityRoleBasedLogin</finalName>
	</build>
</project>

步驟3: 添加Spring Security配置類

這里最重要一個步驟,在我們的應(yīng)用程序中添加 Spring Security 是來創(chuàng)建Spring Security的Java配置。 這個結(jié)構(gòu)將創(chuàng)建用來負(fù)責(zé)應(yīng)用程序內(nèi)所有安全(保護(hù)應(yīng)用程序的URL,驗(yàn)證提交用戶名和密碼,重定向到日志中的形式等等)的springSecurityFilterChain Servlet過濾程序。

package com.yiibai.springsecurity.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Autowired
	CustomSuccessHandler customSuccessHandler;

	@Autowired
	public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().withUser("yiibai").password("123456").roles("USER");
		auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
		auth.inMemoryAuthentication().withUser("dba").password("123456").roles("ADMIN","DBA");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
	  http.authorizeRequests()
	  	.antMatchers("/", "/home").access("hasRole('USER')")
	  	.antMatchers("/admin/**").access("hasRole('ADMIN')")
	  	.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
	  	.and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
	  	.usernameParameter("ssoId").passwordParameter("password")
	  	.and().csrf()
	  	.and().exceptionHandling().accessDeniedPage("/Access_Denied");
	}

}

這個類是類似的,只不過有一些重要區(qū)別于之前教程文章:
formLogin().loginPage("/login").successHandler(customSuccessHandler). 現(xiàn)在來看看 successHandler。這個類[如下所示]負(fù)責(zé)基于自定義邏輯的重定向,這對我們來說是根據(jù)他的角色[USER / ADMIN / DBA]用戶重定向[ home/admin/db] 

以上的安全配置使用XML配置格式如下所示:
<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-4.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd">
     
    <http auto-config="true" >
        <intercept-url pattern="/" access="hasRole('USER')" />
        <intercept-url pattern="/home" access="hasRole('USER')" />
        <intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
        <intercept-url pattern="/dba**" access="hasRole('ADMIN') and hasRole('DBA')" />
        <form-login  login-page="/login" 
                     username-parameter="ssoId" 
                     password-parameter="password" 
                     authentication-success-handler-ref="customSuccessHandler"
                     authentication-failure-url="/Access_Denied" />
        <csrf/>
    </http>
 
    <authentication-manager >
        <authentication-provider>
            <user-service>
                <user name="yiibai"  password="123456"  authorities="ROLE_USER" />
                <user name="admin" password="123456" authorities="ROLE_ADMIN" />
                <user name="dba"   password="123456" authorities="ROLE_ADMIN,ROLE_DBA" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
     
    <beans:bean id="customSuccessHandler" class="com.websystique.springsecurity.configuration.CustomSuccessHandler" />
    
</beans:beans>

下面是上面提及成功處理的類,如下圖的所示 - 

package com.websystique.springsecurity.configuration;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	@Override
	protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
			throws IOException {
		String targetUrl = determineTargetUrl(authentication);

		if (response.isCommitted()) {
			System.out.println("Can't redirect");
			return;
		}

		redirectStrategy.sendRedirect(request, response, targetUrl);
	}

	/*
	 * This method extracts the roles of currently logged-in user and returns
	 * appropriate URL according to his/her role.
	 */
	protected String determineTargetUrl(Authentication authentication) {
		String url = "";

		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

		List<String> roles = new ArrayList<String>();

		for (GrantedAuthority a : authorities) {
			roles.add(a.getAuthority());
		}

		if (isDba(roles)) {
			url = "/db";
		} else if (isAdmin(roles)) {
			url = "/admin";
		} else if (isUser(roles)) {
			url = "/home";
		} else {
			url = "/accessDenied";
		}

		return url;
	}

	private boolean isUser(List<String> roles) {
		if (roles.contains("ROLE_USER")) {
			return true;
		}
		return false;
	}

	private boolean isAdmin(List<String> roles) {
		if (roles.contains("ROLE_ADMIN")) {
			return true;
		}
		return false;
	}

	private boolean isDba(List<String> roles) {
		if (roles.contains("ROLE_DBA")) {
			return true;
		}
		return false;
	}

	public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
		this.redirectStrategy = redirectStrategy;
	}

	protected RedirectStrategy getRedirectStrategy() {
		return redirectStrategy;
	}

} 
注意:看看如何擴(kuò)展 SimpleUrlAuthenticationSuccessHandler類和overridinghandle()方法,這個方法簡單地使用配置RedirectStrategy[默認(rèn)在這種情況下]調(diào)用重定向,由用戶定義determineTargetUrl方法返回URL。此方法提取當(dāng)前認(rèn)證對象用戶記錄的角色,然后構(gòu)造基于角色有相應(yīng)的URL。最后RedirectStrategy,這是負(fù)責(zé) Spring Security 框架內(nèi)的所有重定向,請求重定向到指定的URL。
以下步驟:其余的都是和之前教程文章中的內(nèi)容相同了。

第4步:使用war注冊springSecurityFilter

下面指定的初始化類使用應(yīng)用程序 war [步驟3中創(chuàng)建] 來注冊springSecurityFilter 。
package com.websystique.springsecurity.configuration;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

}
上面在XML配置格式中的設(shè)置為:
<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>

第5步: 添加控制器

package com.yiibai.springsecurity.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloWorldController {

	
	@RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
	public String homePage(ModelMap model) {
		model.addAttribute("user", getPrincipal());
		return "welcome";
	}

	@RequestMapping(value = "/admin", method = RequestMethod.GET)
	public String adminPage(ModelMap model) {
		model.addAttribute("user", getPrincipal());
		return "admin";
	}
	
	@RequestMapping(value = "/db", method = RequestMethod.GET)
	public String dbaPage(ModelMap model) {
		model.addAttribute("user", getPrincipal());
		return "dba";
	}

	@RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
	public String accessDeniedPage(ModelMap model) {
		model.addAttribute("user", getPrincipal());
		return "accessDenied";
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String loginPage() {
		return "login";
	}

	@RequestMapping(value="/logout", method = RequestMethod.GET)
	public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (auth != null){    
			new SecurityContextLogoutHandler().logout(request, response, auth);
		}
		return "redirect:/login?logout";
	}

	private String getPrincipal(){
		String userName = null;
		Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

		if (principal instanceof UserDetails) {
			userName = ((UserDetails)principal).getUsername();
		} else {
			userName = principal.toString();
		}
		return userName;
	}

}

第6步: 添加SpringMVC配置類

package com.spring.springsecurity.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.yiibai.springsecurity")
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter{
	
	@Bean
	public ViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");

		return viewResolver;
	}

     /*
     * Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
    }
}

第7步: 添加初始化類

package com.yiibai.springsecurity.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] { HelloWorldConfiguration.class };
	}
 
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}
 
	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

}

第8步: 添加視圖

login.jsp

這個視圖中還包含了用于登錄面板布局CSS。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<title>Login page</title>
		<link href="<c:url value='/static/css/bootstrap.css' />"  rel="stylesheet"></link>
		<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
		<link rel="stylesheet" type="text/css"  />
	</head>

	<body>
		<div id="mainWrapper">
			<div class="login-container">
				<div class="login-card">
					<div class="login-form">
						<c:url var="loginUrl" value="/login" />
						<form action="${loginUrl}" method="post" class="form-horizontal"><c:if test="${param.error != null}">	<div class="alert alert-danger">		<p>Invalid username and password.</p>	</div></c:if><c:if test="${param.logout != null}">	<div class="alert alert-success">		<p>You have been logged out successfully.</p>	</div></c:if><div class="input-group input-sm">	<label class="input-group-addon" for="username"><i class="fa fa-user"></i></label>	<input type="text" class="form-control" id="username" name="ssoId" placeholder="Enter Username" required></div><div class="input-group input-sm">	<label class="input-group-addon" for="password"><i class="fa fa-lock"></i></label> 	<input type="password" class="form-control" id="password" name="password" placeholder="Enter Password" required></div><input type="hidden" name="${_csrf.parameterName}"  value="${_csrf.token}" />	<div class="form-actions">	<input type="submit"		class="btn btn-block btn-primary btn-default" value="Log in"></div>
						</form>
					</div>
				</div>
			</div>
		</div>

	</body>
</html>
正如在之前的教程文章提到的還有,CSRF關(guān)聯(lián)上述JSP行:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /></strong>

這是需要用來防止跨站請求偽造攻擊。正如您所看到的,CSRF參數(shù)可在JSP EL表達(dá)式訪問,還可以通過添加以下到JSP的頂部來強(qiáng)行執(zhí)行EL表達(dá)式計算:

<%@ page isELIgnored="false"%>

welcome.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>Welcome page</title>
</head>
<body>
	Dear <strong>${user}</strong>, Welcome to Home Page.
	<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

admin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>Admin page</title>
</head>
<body>
	Dear <strong>${user}</strong>, Welcome to Admin Page.
	<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

dba.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>DBA page</title>
</head>
<body>
	Dear <strong>${user}</strong>, Welcome to DBA Page.
	<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

accessDenied.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>AccessDenied page</title>
</head>
<body>
	Dear <strong>${user}</strong>, You are not authorized to access this page
	<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>
這里是快速一個CSS布局,只是大概進(jìn)行美化,當(dāng)然你也可以做得更漂亮一些。

app.css

html{
	background-color:#2F2F2F;
}

body, #mainWrapper {
	height: 100%;
}

body, #mainWrapper, .form-control{
	font-size:14px!important;
}

#mainWrapper {
	height: 100vh; 
	padding-left:10px;
	padding-right:10px;
	padding-bottom:10px;
}

#authHeaderWrapper{
	clear:both;
	width: 100%;
	height:3%;
	padding-top:5px;
	padding-bottom:5px;
}

.login-container {
    margin-top: 100px;
    background-color: floralwhite;
    width: 40%;
    left: 30%;
    position: absolute;
}

.login-card {
    width: 80%;
    margin: auto;
}
.login-form {
    padding: 10%;
}

第9步:構(gòu)建和部署應(yīng)用程序

現(xiàn)在構(gòu)造 war(通過 eclipse/m2eclipse)或通過Maven的命令行(mvn clean install)。部署WAR文件到Servlet3.0容器。由于這里我使用的是在 eclipse 中配置 Tomcat,可以直接發(fā)布到 Tomcat 服務(wù)容器中。如果不知道怎么使用,可以參考:http://www.yiibai.com/maven/create-a-maven-web-project-with-eclipse.html

運(yùn)行應(yīng)用程序
打開瀏覽器,然后訪問:http://localhost:8080/SpringSecurityRoleBasedLogin ,如果沒有任何錯誤,將提示登錄 - 

DBA提供的憑據(jù)(用戶名和密碼登錄),如下所示:

提交,您將會被跳轉(zhuǎn)到 /db 頁面,作為登錄的用戶具有DBA角色(基于角色的登錄)。如下圖中所示 - 

現(xiàn)在注銷,并填寫用戶(yiibai)角色的憑據(jù)(使用用戶名和密碼)登錄。

首先提供一個錯誤的密碼,然后點(diǎn)擊登錄 - 

提供正確的用戶角色的憑據(jù),您將被重定向到主頁。

現(xiàn)在嘗試訪問管理頁面。您應(yīng)該看到拒絕訪問頁面。

現(xiàn)在,注銷并使用管理員憑據(jù)登錄,您將會被重向到URL:/admin 。如下圖中所示 - 

用戶 admin 登錄成功后的頁面,如下所示 - 

到這里就結(jié)束了。在下一篇文章中將學(xué)習(xí)使用基于Hibernate的注解方法的Spring Security數(shù)據(jù)庫認(rèn)證

下載源代碼

07-SpringSecurityRoleBasedLogin.zip

參考