דוגמה לאימות גישה והרשאה מבוסס תפקיד ב-Spring Security

היום נתעקב אחרי דוגמא לכניסה והרשאה במערכת האבטחה של Spring המבוססת על תפקידים. עם זאת, לפני שתתחיל לקרוא את הפוסט הזה, אנא עבור על הפוסט הקודם שלי על "דוגמא להתחברות והתנתקות ב- Spring 4 Security MVC" כדי להשגיח מעט יותר על הידע הבסיסי שלך על Spring 4 Security.

תפקיד אבטחת Spring

בפוסט הזה, אנו נדבר על איך להגדיר, להשתמש ולנהל תפקידי אבטחה של Spring כמו "USER", "ADMIN" באפליקציה ווב של Spring. כמו בפוסט הקודם שלי, גם בדוגמא הזו אנו משתמשים ב- Spring 4 MVC Security עם In-Memory Store ובתכונת Spring Java Configuration לפיתוח האפליקציה. זה אומר שאנו לא משתמשים בקובץ web.xml וגם לא כותבים שורה אחת של קונפיגורציה ב-XML של Spring. אנו משתמשים באפשרות "In-Memory Store" כדי לאחסן ולנהל אישורי משתמש. אנו משתמשים ב- Spring 4.0.2.RELEASE, Spring STS 3.7 Suite IDE, Spring TC Server 3.1 עם Java 1.8 ובכלי הבנייה של Maven כדי לפתח את הדוגמא הזו.

דוגמא לגישה והרשאה במערכת האבטחה של Spring על פי תפקיד

  1. צור פרויקט "Simple Spring Web Maven" ב- Spring STS Suite עם הפרטים הבאים.
    שם הפרויקט: SpringMVCSecruityMavenRolesApp2. השתמש באותו קובץ pom.xml מהפוסט הקודם שלי עם השינויים הבאים
<artifactId>SpringMVCSecruityMavenRolesApp</artifactId>

<build>
  <finalName>SpringMVCSecruityMavenRolesApp</finalName>
</build>
</project>
  1. השתמש בכל הקבצים של Java ו-JSP מהפוסט הקודם שלי. נדבר רק על תוכן שעודכן או נוסף כאן.
  2. עדכן את קובץ LoginSecurityConfig.java כדי להגדיר תפקידי משתמש כמו "USER" ו-"ADMIN".
    LoginSecurityConfig.java
package com.journaldev.spring.secuity.config;

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 LoginSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
		authenticationMgr.inMemoryAuthentication()
			.withUser("jduser").password("jdu@123").authorities("ROLE_USER")
			.and()
			.withUser("jdadmin").password("jda@123").authorities("ROLE_USER","ROLE_ADMIN");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		
		http.authorizeRequests()
			.antMatchers("/homePage").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
			.antMatchers("/userPage").access("hasRole('ROLE_USER')")
			.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
			.and()
				.formLogin().loginPage("/loginPage")
				.defaultSuccessUrl("/homePage")
				.failureUrl("/loginPage?error")
				.usernameParameter("username").passwordParameter("password")				
			.and()
				.logout().logoutSuccessUrl("/loginPage?logout"); 
		
	}
}

הסבר קוד

  1. בשיטה configureGlobal(), הוספנו שני משתמשים: משתמש עם תפקיד "ROLE_USER" ומשתמש עם תפקידי "ROLE_USER" ו-"ROLE_ADMIN". כלומר, המשתמש השני ישמש כמשתמש מנהל. בכך אנו יכולים להגדיר כמות כלשהי של משתמשים ותפקידים.
  2. ניתן להשתמש בשיטות authorities(ROLE) או roles(ROLE) להגדיר תפקידים ביישום שלנו.
  3. ההבחנה בין authorities() ו-roles():
  • authorities() דורש כתובת מלאה כמו "ROLE_USER"
  • roles() דורש שם תפקיד כמו "USER". הוא יוסיף באופן אוטומטי את הערך "ROLE_" לשם תפקיד זה "USER".
  1. בשיטה configure(), הגדרנו כתובות URL שונות עם גישה דרושה לתפקידים מסוימים.
antMatchers("/homePage")
   .access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")

מקטע קוד זה מגדיר ש-"/homePage" זמין עבור תפקידי USER ו-ADMIN כאחד.

 .antMatchers("/userPage").access("hasRole('ROLE_USER')")
 .antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")

מקטע קוד זה מגדיר ש-"/userPage" נגיש רק על ידי תפקיד "USER" ו-"/adminPage" נגיש רק על ידי תפקיד "ADMIN". אם משתמשים בתפקידים אחרים נגשים לדפים אלו, נקבל הודעת שגיאה "403 Access is Denied".

  1. עדכן את קובץ הבקר LoginController.java כפי שמוצג למטה.
    LoginController.java
package com.journaldev.spring.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

	@RequestMapping(value = { "/"}, method = RequestMethod.GET)
	public ModelAndView welcomePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("welcomePage");
		return model;
	}

	@RequestMapping(value = { "/homePage"}, method = RequestMethod.GET)
	public ModelAndView homePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("homePage");
		return model;
	}
	
	@RequestMapping(value = {"/userPage"}, method = RequestMethod.GET)
	public ModelAndView userPage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("userPage");
		return model;
	}
	
	@RequestMapping(value = {"/adminPage"}, method = RequestMethod.GET)
	public ModelAndView adminPage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("adminPage");
		return model;
	}
	
	@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
	public ModelAndView loginPage(@RequestParam(value = "error",required = false) String error,
	@RequestParam(value = "logout",	required = false) String logout) {
		
		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error", "Invalid Credentials provided.");
		}

		if (logout != null) {
			model.addObject("message", "Logged out from JournalDEV successfully.");
		}

		model.setViewName("loginPage");
		return model;
	}

}

הסבר קוד בנוסף לדוגמה הקודמת, כאן הוספנו שני נתיבי URL חדשים נוספים.

  1. "/userPage" משמש על ידי תפקיד המשתמש לגישה וביצוע פעילויות משתמש רגיל.

  2. "/adminPage" משמש על ידי תפקיד המנהל לגישה וביצוע פעילויות מנהל מערכת. תפקיד המנהל יכול לגשת גם ל-URL "/userPage".

  3. עדכנו את קובץ הדף הבית homePage.jsp כדי לספק פעילויות מסוימות עבור משתמש רגיל ומנהל מערכת.
    homePage.jsp

<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<a href="${pageContext.request.contextPath}/userPage">JD User</a> | <a href="${pageContext.request.contextPath}/adminPage">JD Admin</a> | <a href="javascript:document.getElementById('logout').submit()">Logout</a>

<h3>Welcome to JournalDEV Tutorials</h3>
<ul>
   <li>Java 8 tutorial</li>
   <li>Spring tutorial</li>
   <li>Gradle tutorial</li>
   <li>BigData tutorial</li>
</ul>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>

כאן הוספנו שלושה אפשרויות לתפריט במערך העליון. "התנתקות" כבר נדונה בפוסט הקודם שלי. שני קישורים חדשים הם:

  1. JD משתמש: נגיש על ידי שני תפקידים – "משתמש" ו-"מנהל"
  2. JD מנהל: נגיש רק על ידי שני תפקידים – "מנהל"

הערה:- ביישומי זמן אמת, נציג רק את הקישור "JD משתמש" לתפקיד "משתמש" ונסתיר את הקישור "JD מנהל". כדי לבדוק האם הוא נגיש על ידי תפקיד "משתמש" או לא וגם לראות את הודעת השגיאה המדויקת, לא הסתרנו את הקישור הזה.20. הוסף קובץ adminPage.jsp חדש כדי לשמש כדף בית עבור תפקיד "מנהל".
adminPage.jsp

<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>Admin Page</h3>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
	<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
  1. הוסף קובץ userPage.jsp חדש כדי לשמש כעמוד הבית עבור תפקיד "USER".
    userPage.jsp
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>User Page</h3>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
	<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>

סיימנו את פיתוח האפליקציה שלנו כעת. הגיע הזמן לראות את מבנה הפרויקט הסופי שלנו ולבדוק את האפליקציה. 26. מבנה הפרויקט הסופי נראה כך:

Spring Security Roles Example Application Test

  1. לחץ ימינה על הפרויקט ב- Spring STS IDE ובחר "הפעל כתיקלוט" >> אפשרות הרצה על שרת". ייבוא דף הבית הברירתי של היישום כפי שמוצג למטה: 3. לחץ על קישור "התחברות ל-JournalDEV". כעת אתה בדף ההתחברות. 5. התחבר לראשונה עם פרטי כניסה בתפקיד "USER":
    שם משתמש: jduser סיסמה: jdu@123 כעת נראה את דף הבית של היישום עם 3 אפשרויות תפריט: "JD User", "JD Admin" ו-"Logout". לחץ על הקישור "JD User". ככל שנכנסנו ליישום באמצעות פרטי כניסה בתפקיד "USER", אנו יכולים לגשת לקישור זה כפי שמוצג למטה. השתמש בחץ לאחור ב- Spring STS IDE והפעם לחץ על הקישור "JD Admin". מכיוון שנכנסנו עם פרטי כניסה בתפקיד "USER", אין לנו גישה לקישור זה. לכן ראינו הודעת שגיאה זו: "403 גישה נדחתה". 9. התחבר שוב והתחבר מחדש עם פרטי כניסה בתפקיד ADMIN:
    שם משתמש: jdadmin סיסמה: jda@123 הפעם נוכל לגשת בהצלחה לקישור "JD Admin" כפי שמוצג למטה. בדוק את הקישור "Logout" כדי להתנתק מהיישום.

כל זה על דוגמת תפקידי אבטחת Spring כדי לספק גישה מאושרת לדפי אפליקציה ווב.

Source:
https://www.digitalocean.com/community/tutorials/spring-security-role-based-access-authorization-example