يوفر Spring Security طرقًا لأداء المصادقة والتفويض في تطبيق الويب. يمكننا استخدام Spring Security في أي تطبيق ويب يعتمد على سيرفلت.
Spring Security
بعض فوائد استخدام Spring Security هي:
- تكنولوجيا مثبتة، فمن الأفضل استخدام هذا بدلاً من إعادة اختراع العجلة. الأمان هو شيء نحتاج إلى العناية الإضافية به، وإلا فستكون تطبيقنا عرضة للهجمات.
- يمنع بعض الهجمات الشائعة مثل CSRF، هجمات تثبيت الجلسة.
- سهل الدمج في أي تطبيق ويب. لا نحتاج إلى تعديل تكوينات تطبيق الويب، يقوم Spring تلقائيًا بحقن مرشحات الأمان إلى تطبيق الويب.
- يوفر دعمًا للمصادقة بطرق مختلفة – في الذاكرة، DAO، JDBC، LDAP والعديد من الطرق الأخرى.
- يوفر خيارًا لتجاهل أنماط عناوين URL محددة، مما يعد جيدًا لخدمة ملفات HTML وصور ثابتة.
- دعم للمجموعات والأدوار.
مثال على Spring Security
تم إنشاء تطبيق ويب ودمجه مع Spring Security. أنشئ تطبيق ويب باستخدام الخيار “Dynamic Web Project” في Eclipse لضمان أن تكون هيكلية التطبيق جاهزة. تأكد من تحويله إلى مشروع Maven لأننا نستخدم Maven لعمليات البناء والنشر. إذا كنت غير ملم بتلك الخطوات، يرجى الرجوع إلى Java Web Application Tutorial. عندما نضمن تأمين التطبيق، ستكون هيكلية المشروع النهائية كما في الصورة أدناه. سنتناول ثلاث طرق لمصادقة Spring Security.
- في الذاكرة
- DAO
- JDBC
بالنسبة لـ JDBC، أقوم باستخدام قاعدة بيانات MySQL وتنفيذ البرنامج النصي التالي لإنشاء جداول تفاصيل المستخدم.
CREATE TABLE `Employees` (
`username` varchar(20) NOT NULL DEFAULT '',
`password` varchar(20) NOT NULL DEFAULT '',
`enabled` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Roles` (
`username` varchar(20) NOT NULL DEFAULT '',
`role` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`username`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Employees` (`username`, `password`, `enabled`)
VALUES
('pankaj', 'pankaj123', 1);
INSERT INTO `Roles` (`username`, `role`)
VALUES
('pankaj', 'Admin'),
('pankaj', 'CEO');
commit;
سنحتاج أيضًا لتكوين مصدر بيانات JDBC كـ JNDI في حاوي الخدمات الخاص بنا، لمعرفة المزيد حول هذا يرجى قراءة Tomcat JNDI DataSource Example.
تبعيات Maven لـ Spring Security
هذا هو ملف pom.xml النهائي الخاص بنا.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>WebappSpringSecurity</groupId>
<artifactId>WebappSpringSecurity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- Spring Security Artifacts - START -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<!-- Spring Security Artifacts - END -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
لدينا الاعتمادات التالية المتعلقة بإطار العمل Spring.
- spring-jdbc: يستخدم لعمليات JDBC بواسطة طريقة المصادقة JDBC. يتطلب إعداد DataSource كـ JNDI. لمثال كامل عن استخدامه، يرجى الرجوع إلى مثال Spring DataSource JNDI
- spring-security-taglibs: مكتبة وسوم Spring Security، لقد استخدمتها لعرض أدوار المستخدم في صفحة JSP. في معظم الأحيان، لن تحتاج إليها على الرغم من ذلك.
- spring-security-config: يُستخدم لتكوين موفرات المصادقة، سواء كان الأمر يتعلق بـ JDBC، DAO، LDAP، إلخ.
- spring-security-web: يُدمج هذا المكون Spring Security مع واجهة برمجة التطبيقات Servlet. نحتاج إليه لتوصيل تكوين الأمان في تطبيق الويب الخاص بنا.
كما لاحظ أننا سنستخدم ميزة واجهة برمجة التطبيقات Servlet 3.0 لإضافة مستمع ومرشحات بشكل برمجي، لهذا يجب أن تكون إصدارات واجهة برمجة التطبيقات Servlet في الاعتماديات 3.0 أو أعلى.
مثال على صفحات عرض أمان Spring
لدينا صفحات JSP و HTML في تطبيقنا. نريد تطبيق المصادقة في جميع الصفحات باستثناء صفحات HTML. health.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Health Check</title>
</head>
<body>
<h3>Service is up and running!!</h3>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="https://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Home Page</title>
</head>
<body>
<h3>Home Page</h3>
<p>
Hello <b><c:out value="${pageContext.request.remoteUser}"/></b><br>
Roles: <b><sec:authentication property="principal.authorities" /></b>
</p>
<form action="logout" method="post">
<input type="submit" value="Logout" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</body>
</html>
I have included index.jsp
as welcome-file
in the application deployment descriptor. Spring Security takes care of CSRF attack, so when we are submitting form for logout, we are sending the CSRF token back to server to delete it. The CSRF object set by Spring Security component is _csrf and we are using it’s property name and token value to pass along in the logout request. Let’s look at the Spring Security configurations now.
تنفيذ مثال Spring Security UserDetailsService DAO
نظرًا لأننا سنستخدم المصادقة القائمة على DAO أيضًا ، فإننا بحاجة إلى تنفيذ واجهة UserDetailsService
وتوفير التنفيذ لأسلوب loadUserByUsername()
. في الواقع ، يجب أن نستخدم بعض المصادر للتحقق من صحة المستخدم ، ولكن للبساطة فقط ، أقوم بعمل التحقق الأساسي. AppUserDetailsServiceDAO.java
package com.journaldev.webapp.spring.dao;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class AppUserDetailsServiceDAO implements UserDetailsService {
protected final Log logger = LogFactory.getLog(getClass());
@Override
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException {
logger.info("loadUserByUsername username="+username);
if(!username.equals("pankaj")){
throw new UsernameNotFoundException(username + " not found");
}
// إنشاء تفاصيل مستخدم وهمية ، يجب القيام بعمليات JDBC
return new UserDetails() {
private static final long serialVersionUID = 2059202961588104658L;
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return "pankaj123";
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List auths = new java.util.ArrayList();
auths.add(new SimpleGrantedAuthority("admin"));
return auths;
}
};
}
}
لاحظ أنني أقوم بإنشاء فئة داخلية مجهولة من UserDetails
وإرجاعها. يمكنك إنشاء فئة تنفيذ لها ثم إنشاؤها وإرجاعها. عادةً ما يكون هذا هو الطريقة المثلى في التطبيقات الفعلية.
تنفيذ مثال Spring Security WebSecurityConfigurer
يمكننا تنفيذ واجهة WebSecurityConfigurer
أو يمكننا تمديد فئة التنفيذ الأساسية WebSecurityConfigurerAdapter
وتجاوز الأساليب. SecurityConfig.java
package com.journaldev.webapp.spring.security;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import com.journaldev.webapp.spring.dao.AppUserDetailsServiceDAO;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
// مصادقة في الذاكرة
// auth.inMemoryAuthentication().withUser("pankaj").password("pankaj123").roles("USER");
// باستخدام مخصص UserDetailsService DAO
// auth.userDetailsService(new AppUserDetailsServiceDAO());
// باستخدام JDBC
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx
.lookup("java:/comp/env/jdbc/MyLocalDB");
final String findUserQuery = "select username,password,enabled "
+ "from Employees " + "where username = ?";
final String findRoles = "select username,role " + "from Roles "
+ "where username = ?";
auth.jdbcAuthentication().dataSource(ds)
.usersByUsernameQuery(findUserQuery)
.authoritiesByUsernameQuery(findRoles);
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
// يجب على Spring Security تجاهل تمامًا عناوين URL التي تنتهي بـ .html
.antMatchers("/*.html");
}
}
يرجى ملاحظة أننا نتجاهل جميع ملفات HTML عن طريق تجاوز configure(WebSecurity web)
الطريقة. يوضح الكود كيفية توصيل مصادقة JDBC. يجب علينا تكوينها عن طريق توفير مصدر البيانات. نظرًا لأننا نستخدم جداولًا مخصصة ، يتعين علينا أيضًا توفير استعلامات الاختيار للحصول على تفاصيل المستخدم والأدوار الخاصة به. تكوين المصادقة في الذاكرة والقائمة على DAO سهل ، حيث تم تعليقهم في الكود أعلاه. يمكنك إلغاء تعليقها لاستخدامها ، وتأكد من وجود تكوين واحد في وقت واحد. الإعلانات @Configuration
و @EnableWebSecurity
مطلوبة ، بحيث يعلم إطار الربيع أن هذه الفئة ستستخدم لتكوين أمان الربيع. تستخدم تكوين أمان الربيع نمط نمط البناء وبناءً على طريقة المصادقة ، قد لا تكون بعض الطرق متاحة لاحقًا. على سبيل المثال ، تعيد auth.userDetailsService()
مثيلًا لـ UserDetailsService
وبعد ذلك لا يمكننا الحصول على أي خيارات أخرى ، مثل عدم إمكاننا تعيين مصدر البيانات بعد ذلك.
دمج أمان Spring Security Web مع واجهة برمجة التطبيقات Servlet
الجزء الأخير هو دمج صف الضبط الخاص بأمان Spring مع واجهة برمجة التطبيقات Servlet. يمكن القيام بذلك بسهولة عن طريق تمديد صف AbstractSecurityWebApplicationInitializer
وتمرير صف ضبط الأمان في بناء الفئة الأم. SecurityWebApplicationInitializer.java
package com.journaldev.webapp.spring.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
عند بدء تشغيل سياقنا، يستخدم ServletContext لإضافة مستمع ContextLoaderListener وتسجيل صف ضبطنا كـ مرشح Servlet. لاحظ أن هذا سيعمل فقط في حاويات السيرفليت التي تتوافق مع Servlet-3. لذا، إذا كنت تستخدم Apache Tomcat، تأكد من أن إصداره 7.0 أو أعلى. مشروعنا جاهز، فقط قم بنشره في حاوي السيرفليت المفضل لديك. أنا استخدم Apache Tomcat-7 لتشغيل هذا التطبيق. توضح الصور أدناه الاستجابة في مختلف السيناريوهات.
الوصول إلى صفحة HTML بدون أمان
فشل المصادقة بسبب بيانات اعتماد سيئة
الصفحة الرئيسية مع مصادقة JDBC الخاصة بأمان Spring
صفحة البداية مع مصادقة DAO لأمان Spring
صفحة البداية مع مصادقة في الذاكرة لأمان Spring
صفحة تسجيل الخروج
إذا كنت ترغب في استخدام حاوية Servlet التي لا تدعم مواصفات Servlet 3، فيجب عليك تسجيل
DispatcherServlet
من خلال وصف النشر. راجع JavaDoc لـ WebApplicationInitializer
للحصول على مزيد من التفاصيل. هذا كل شيء بالنسبة لدورة دراسية مثالية عن أمان Spring وتكاملها في تطبيق ويب قائم على Servlet. يمكنك تنزيل مشروع العينة من الرابط أدناه واستكشافه للمزيد.
Source:
https://www.digitalocean.com/community/tutorials/spring-security-example-tutorial