這篇文章主要記錄如何在Spring MVC專案中使用Spring Security模組來做Access的保護
一樣會透過一個簡單範例來介紹:
首先,一樣先放上最後建立好的專案結構,如下所示 :
1. 先在NetBeans中建立Maven的Web專案,並在pom.xml中加入所需的dependency
<!-- 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>
2. 建立Spring Security要用到的Configuration Class (SecurityConfiguration.java)
package allen0818.practice.springsecurityprac.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; /** * * @author allen0818 */ @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("allen0818").password("123456").roles("USER"); auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN"); auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA");//dba have two roles. } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/home").permitAll() .antMatchers("/admin/**").access("hasRole('ADMIN')") .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") .and().formLogin() .and().exceptionHandling().accessDeniedPage("/Access_Denied"); } } 在這邊程式做了幾件事: (1) 在configureGlobalSecurity這個方法中為特定的使用者綁定相對應的Role (2) 這邊採的是inMemory的認證機制,另外還有提供JDBC, LDAP ...等其他認證機制 (3) 在configure這個方法中我們設定了哪些路徑下只允許擁有哪些Role的使用者進入,permitAll()表示不用驗證可直接進入 (4) .formLogin()方法可讓Spring Security自動產生一個簡易的使用者認證頁面 (5) .exceptionHandling().accessDeniedPage用來導向所有產生403錯誤的頁面(即Access Denied的錯誤)
3. 建立一個Initial Class (SecurityWebApplicationInitializer.java),以註冊剛剛所建立的Configuration
package allen0818.practice.springsecurityprac.configuration; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; /** * * @author allen0818 */ public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
4. 加入Controller (HelloWorldController.java)
package allen0818.practice.springsecurityprac.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; /** * * @author allen0818 */ @Controller public class HelloWorldController { @RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET) public String homePage(ModelMap model) { model.addAttribute("greeting", "Hi, Welcome to mysite. "); 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="/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 "welcome"; } @RequestMapping(value = "/Access_Denied", method = RequestMethod.GET) public String accessDeniedPage(ModelMap model) { model.addAttribute("user", getPrincipal()); return "accessDenied"; } 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; } } 從程式碼中可以看到Spring Security如何封裝使用者的驗證資訊,並提供了Logout的方法
5. 建立Spring MVC的Configuration Class (SpringMvcConfiguration.java)
package allen0818.practice.springsecurityprac.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.view.JstlView; /** * * @author allen0818 */ @Configuration @EnableWebMvc @ComponentScan(basePackages = "allen0818.practice.springsecurityprac") public class SpringMvcConfiguration { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
6. 建立Spring MVC的Initializer Class(SpringMvcInitializer.java)
package allen0818.practice.springsecurityprac.configuration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; /** * * @author allen0818 */ public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { SpringMvcConfiguration.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
7. 加入前端顯示所需要的views
welcome.jsp
<%-- Document : welcome Created on : 2016/5/11, 下午 04:06:15 Author : allen0818 --%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Welcome Page</title> </head> <body> Greeting : ${greeting} This is a welcome page. </body> </html>
admin.jsp
<%-- Document : admin Created on : 2016/5/11, 下午 04:06:02 Author : allen0818 --%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <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
<%-- Document : dba Created on : 2016/5/11, 下午 04:06:08 Author : allen0818 --%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <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
<%-- Document : accessDenied Created on : 2016/5/11, 下午 04:05:53 Author : allen0818 --%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <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>
呈現畫面如下(請觀察上方網址列):
1. welcome page
2. Spring Security提供的登入頁面
3. 登入失敗畫面
4. 進入未授權頁面畫面
5. Logout後導回至Welcome Page
還蠻多東西的,初次接觸的朋友可能需要好好消化一下~
這篇文章的內容主要是參考這篇教學整理而成,若需要有關Spring Security更詳細的介紹
可以參考官方釋出的document
參考資料:
1. http://websystique.com/spring-security/spring-security-4-hello-world-annotation-xml-example/
2. http://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/