التسجيل والترخيص باستخدام Spring Security باستخدام تطبيق بسيط كمثال

مساء الخير

تتناول هذه المقالة إنشاء تطبيق ويب بسيط باستخدام Spring Boot و Spring Security. سيقوم التطبيق بتنفيذ تسجيل المستخدمين الجدد والترخيص ، وتقييد الوصول إلى صفحات الموقع اعتمادا على دور المستخدم.
الهدف الرئيسي من المقالة هو إظهار كيف يمكنك تقييد الوصول إلى صفحات مختلفة من الموقع للمستخدمين ذوي الأدوار المختلفة.

ماذا سيكون التطبيق؟


موقع الويب مع الصفحات التالية:

  • الصفحات التي يمكن لجميع المستخدمين الوصول إليها: المنزل والتسجيل وتسجيل الدخول ؛
  • الصفحة متاحة للمستخدمين المسجلين: الأخبار ؛
  • صفحة المشرف المتاحة.

ما سوف نستخدمه


  • JDK 8+ ؛
  • انتليج ايديا
  • Spring (Spring Spring، Spring MVC، Spring Security)؛
  • السبات.
  • JSP.
  • كيو.

محتوى


  1. وصف الشروح الرئيسية المستخدمة.
  2. إنشاء مشروع جديد في IDE.
  3. إنشاء هيكل المشروع (الحزم).
  4. إضافة الكيانات ، وحدات التحكم ، الخدمات ، المستودعات ، المشاهدات.
  5. إطلاق التطبيق.

1. وصف الشروح الرئيسية المستخدمة


جهاز التحكم هو نوع خاص من الفصل المستخدم في تطبيقات MVC. يبدو وكأنه servlet HttpServlet العادية التي تعمل مع كائنات HttpServletRequest و HttpServletResponse ، ولكن مع ميزات متقدمة من Spring Framework.
المستودع - يشير إلى أن الفصل يستخدم لتحديد القائمة
العمل الضروري للبحث عن البيانات واسترجاعها وتخزينها. يمكن استخدام الشرح لتطبيق قالب DAO.
الخدمة - تشير إلى أن الفصل عبارة عن خدمة لتنفيذ منطق العمل.
التكوين - يستخدم هذا التعليق التوضيحي للفئات التي تحدد مكونات الفول.
Autowired - التعليق التوضيحي يسمح لك بتعيين قيمة الحقل تلقائيًا. تتمثل وظيفة هذا التعليق التوضيحي في أننا لسنا بحاجة إلى القلق بشأن أفضل طريقة لنقل مثيل Bean إلى نسخة أخرى من Bean. سيجد Spring نفسه الفاصوليا المرغوبة ويستبدل قيمتها في الخاصية المميزة بالتعليق التوضيحي.

القليل من المعلومات حول Spring Security


الكائن الأساسي هو SecurityContextHolder . يقوم بتخزين معلومات حول سياق الأمان الحالي للتطبيق ، والذي يتضمن معلومات مفصلة عن المستخدم (الرئيسي) الذي يعمل مع التطبيق. يستخدم Spring Security كائن مصادقة ، مستخدم جلسة مخول.

"المستخدم" هو مجرد كائن. في معظم الحالات ، يمكن أن يكون
يلقي لفئة UserDetails . يمكن اعتبار UserDetails بمثابة محول بين قواعد بيانات المستخدم وما يتطلبه Spring Security داخل SecurityContextHolder .

لإنشاء UserDetails ، يتم استخدام واجهة UserDetailsService ، مع طريقة واحدة:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

2. إنشاء مشروع جديد في IDE


سوف نستخدم نظام بناء Maven.



GroupId تعني المعرف الفريد للشركة (أو اسم نطاقك الشخصي) الذي يصدر المشروع. ArtefactId هو مجرد اسم مشروعنا.



بعد اكتمال إنشاء المشروع ، يتم فتح ملف pom.xml ، ستقدم Idea تمكين الاستيراد التلقائي - لا ترفض. سيحتوي هذا الملف على جميع التبعيات (المكتبات) المستخدمة في المشروع.



3. إنشاء هيكل المشروع (الحزم)


الانتقال الفوري إلى إنشاء الحزم. فيما يلي هيكل المشروع ، الذي يجب أن يظهر.



الآن باختصار حول ما سيتم تخزينه في كل حزمة:

  • src \ main \ java \ com \ boots \ config - فئات مع تكوينات لـ MVC (MvcConfig) والأمان (WebSecurityConfig) ؛
  • src \ main \ java \ com \ boots \ controller - فئات مع وحدات تحكم؛
  • src \ main \ java \ com \ boots \ entity - فئات مع نماذج ؛
  • src \ main \ java \ com \ boots \ repository - واجهات المستودع ؛
  • src \ main \ java \ com \ boots \ service - دروس مع خدمات للنماذج ؛
  • src \ main \ webapp \ resources - كائنات ثابتة: js، css، img؛
  • src \ main \ webapp \ WEB-INF \ jsp - تمثيل في شكل ملفات .jsp.

النظر في ملف pom.xml . في هذا الملف ، تحتاج إلى تحديد رابط للملف الأصل باستخدام العلامة الأصل ، أي ستتم إضافة كافة الخصائص والتبعيات الخاصة بالوالد إلى هذا الملف الفرعي.

 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> </parent> 

بعد ذلك ، أضف التبعيات لوحدات Spring ، برنامج تشغيل قاعدة بيانات PostgreSQL ، خادم Tomcat ، JSTL.

 <properties> <java.version>1.8</java.version> </properties> 

بشكل افتراضي ، سوف يستخدم maven الإصدار القديم من java 1.6 ، لإصلاح ذلك ، نحدد الإصدار بشكل صريح.

 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.8</version> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>9.0.27</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies> 

نضيف أيضًا مكونًا إضافيًا يسمح لك بحزم محفوظات الجرار أو الحرب وتشغيلها "في مكانها":

 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 

pom.xml الكامل
 <?xml version="1.0" encoding="UTF-8"?> <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>ark</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>9.0.27</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 


املأ ملف application.properties . تحتوي الأسطر الثلاثة الأولى على بيانات للاتصال بقاعدة البيانات (اسم قاعدة البيانات هو "الربيع" ، تسجيل الدخول وكلمة المرور). تشير السطوران الأخيرتان إلى المسار إلى ملفات .jsp:
 spring.datasource.url=jdbc:postgresql://localhost/spring spring.datasource.username=postgres spring.datasource.password=password spring.jpa.show-sql=true spring.jpa.generate-ddl=false spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.mvc.view.prefix = /WEB-INF/jsp/ spring.mvc.view.suffix = .jsp 

تعرض خاصية spring.jpa.show-sql نصوص استعلامات قاعدة البيانات إلى وحدة التحكم.
يتيح لك spring.jpa.hibernate.ddl-auto إمكانية إعداد استراتيجية لإنشاء قاعدة بيانات استنادًا إلى الطرز التي نقدمها ؛ وله قيم مختلفة (لا شيء ، أو إنشاء ، أو تحديث ، وما إلى ذلك). يعني التحديث في هذه الحالة أنه سيتم إنشاء جداول وحقول قاعدة البيانات على أساس النماذج الخاصة بنا وسوف تتغير معها.

في المستقبل ، سنحتاج فقط إلى إنشاء قاعدة بيانات تحمل اسم الربيع ، وسيتم إنشاء جداول المستخدم وأدواره وجدول روابطه مع المفاتيح الخارجية تلقائيًا استنادًا إلى الطرز (حزمة الكيان) ، التي سنشرع الآن في إنشائها.

4. إضافة الكيانات ، وحدات التحكم ، الخدمات ، المستودعات ووجهات النظر


4.1. إضافة الكيانات (النماذج)


متطلب إلزامي لجميع الكيانات: الحقول الخاصة ، والأعراس ، والقوائم لجميع الحقول ومنشئ فارغ (غير موضح في الأمثلة). لا يحتاجون إلى الكتابة يدويًا ، اضغط على Alt + Insert وستقوم Idea بذلك نيابةً عنك.

لاستيراد الفئات والمكتبات اللازمة ، استخدم اختصار لوحة المفاتيح Alt + Enter .



المستخدم
 @Entity @Table(name = "t_user") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Size(min=2, message = "  5 ") private String username; @Size(min=2, message = "  5 ") private String password; @Transient private String passwordConfirm; @ManyToMany(fetch = FetchType.EAGER) private Set<Role> roles; public User() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public void setUsername(String username) { this.username = username; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return getRoles(); } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPasswordConfirm() { return passwordConfirm; } public void setPasswordConfirm(String passwordConfirm) { this.passwordConfirm = passwordConfirm; } public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } } 


العضو. في البداية حول التعليقات التوضيحية: يقول الكيان أن حقول الفصل تحتوي على خريطة في قاعدة البيانات ، يشير الجدول (الاسم = "t_user") إلى الجدول.

تشير المعلمة GenerationType.IDENTITY IDENTITY إلى أن قاعدة البيانات ستشارك في إنشاء معرّف. هناك استراتيجيات أخرى. SEQUENCE - يستخدم محرك قاعدة البيانات المدمجة ، مثل PostgreSQL أو Oracle ، وهي آلية لإنشاء قيم متسلسلة (تسلسل). TABLE - يستخدم جدول منفصل مع قيم المفاتيح التي تمت تهيئتها. خيار آخر هو AUTO ، الإسبات نفسه سيختار واحدة من الاستراتيجيات المذكورة أعلاه ، ولكن يوصى بتحديد الاستراتيجية بشكل صريح.

لا يوجد حقل في التعليق التوضيحي المؤقت في قاعدة البيانات. ترتبط قائمة الأدوار بالمستخدم ، والعلاقة بين عدة أطراف (يمكن أن يكون لأحد المستخدمين عدة أدوار من جانب واحد ، ويمكن لدور واحد أن يكون له عدة مستخدمين على الجانب الآخر) ؛ FetchType.EAGER - التنزيل "الجشع" ، أي يتم تحميل قائمة الأدوار على الفور مع المستخدم (لا تنتظر حتى يتم الاتصال بهم).

من أجل زيادة استخدام فئة المستخدم في Spring Security ، يجب أن تقوم بتطبيق واجهة UserDetails . للقيام بذلك ، تجاوز جميع أساليبها. ولكن في المثال الخاص بنا ، سنستخدم طريقة getAuthorities () فقط ، فهي تعرض قائمة بأدوار المستخدم. لذلك ، بالنسبة للطرق المتبقية ، قم بتغيير قيمة الإرجاع إلى صواب .

دور
 @Entity @Table(name = "t_role") public class Role implements GrantedAuthority { @Id private Long id; private String name; @Transient @ManyToMany(mappedBy = "roles") private Set<User> users; public Role() { } public Role(Long id) { this.id = id; } public Role(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } @Override public String getAuthority() { return getName(); } } 


الدور. يجب أن يقوم هذا الفصل بتطبيق واجهة GrantedAuthority ، حيث يجب إعادة تعريف طريقة getAuthority () واحدة فقط (تُرجع اسم الدور). يجب أن يتطابق اسم الدور مع النمط: "ROLE_NAME" ، على سبيل المثال ، ROLE_USER . بالإضافة إلى المُنشئ الافتراضي ، يجب عليك إضافة المزيد من المنشئات العامة: الأول يقبل المعرف الوحيد والمعرف الثاني والاسم.

هنا يمكنك إضافة قيود المجال. الحجم (دقيقة = 2) - يعني أن الحد الأدنى لطول الحقل هو 2 ، في حالة انتهاك التقييد ، سيتم عرض رسالة.

4.2. تطبيق طبقة الوصول إلى البيانات وطبقة الخدمة


يوفر Spring Data مجموعة من التطبيقات الجاهزة لإنشاء طبقة توفر الوصول إلى قاعدة البيانات. توفر واجهة JpaRepository مجموعة من الطرق القياسية (findBy ، حفظ ، deleteById ، وما إلى ذلك) للعمل مع قاعدة البيانات.

UserRepository. ننشئ واجهة المستخدم في حزمة المستودع ونرث JpaRepository <المستخدم ، الطويل> ، ونحدد فئة المستخدم ونوع معرفه طويل .

 public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); } 

وهكذا ببساطة عن طريق إنشاء واجهة ووراثة JpaRepository ، يمكنك تنفيذ استعلامات قاعدة البيانات القياسية. إذا كنت بحاجة إلى طريقة محددة ، فقم فقط بإضافتها إلى الواجهة ، بناءً على نصائح Idea. على سبيل المثال ، نحتاج إلى طريقة للبحث عن مستخدم في قاعدة بيانات بالاسم. نكتب نوع الكائن المرتجع ، ثم يقدم IDE الخيارات الممكنة. أي في هذه الحالة ، يحدد اسم الأسلوب نص الطلب.



إذا لزم الأمر ، يمكنك استخدام التعليق التوضيحي للاستعلام على الطريقة وكتابة الاستعلامات في HQL أو SQL (تحتاج إلى إضافة nativeQuery = true).

 @Query(value = "SELECT nextval(pg_get_serial_sequence('t_user', 'id'))", nativeQuery = true) Long getNextId(); 

RoleRepository. نخلق بنفس الطريقة ، نحن لسنا بحاجة إلى أساليبنا الخاصة هنا.

 public interface RoleRepository extends JpaRepository<Role, Long> { } 

UserService. يحتوي على طرق منطق العمل للتطبيق. تطبق هذه الفئة واجهة UserDetailsService (مطلوبة من أجل Spring Security) ، والتي تحتاج فيها إلى تجاوز أسلوب loadUserByUsername () واحد.

في هذه الفئة ، يمكنك رؤية طريقة أخرى لتنفيذ استعلام SQL - باستخدام EntityManager.

خدمة المستخدم
 @Service public class UserService implements UserDetailsService { @PersistenceContext private EntityManager em; @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; @Autowired BCryptPasswordEncoder bCryptPasswordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return user; } public User findUserById(Long userId) { Optional<User> userFromDb = userRepository.findById(userId); return userFromDb.orElse(new User()); } public List<User> allUsers() { return userRepository.findAll(); } public boolean saveUser(User user) { User userFromDB = userRepository.findByUsername(user.getUsername()); if (userFromDB != null) { return false; } user.setRoles(Collections.singleton(new Role(1L, "ROLE_USER"))); user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); return true; } public boolean deleteUser(Long userId) { if (userRepository.findById(userId).isPresent()) { userRepository.deleteById(userId); return true; } return false; } public List<User> usergtList(Long idMin) { return em.createQuery("SELECT u FROM User u WHERE u.id > :paramId", User.class) .setParameter("paramId", idMin).getResultList(); } } 


النظر في طريقة saveUser (مستخدم المستخدم) .

 public boolean saveUser(User user) { User userFromDB = userRepository.findByUsername(user.getUsername()); if (userFromDB != null) { return false; } user.setRoles(Collections.singleton(new Role(1L, "ROLE_USER"))); user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); return true; } 

أولاً ، يتم إجراء البحث في قاعدة البيانات حسب اسم المستخدم ، إذا كان هناك مستخدم بنفس الاسم بالفعل ، فإن الطريقة تنتهي. إذا لم يتم أخذ اسم المستخدم ، فسيتم إضافة دور ROLE_USER. من أجل عدم تخزين كلمة المرور في نموذج أولي ، تمت تجزئتها مسبقًا باستخدام bCryptPasswordEncoder . ثم يتم حفظ المستخدم الجديد في قاعدة البيانات.

4.3. مضيفا وحدات التحكم


بالنسبة للصفحات التي لا تتم معالجتها بواسطة الخادم بأي طريقة ، ولكن ببساطة ترجع الصفحة ، يمكن تكوين التعيين في التكوين. تتم معالجة صفحة تسجيل الدخول بواسطة وحدة التحكم في Spring Security بشكل افتراضي ، لذلك لا يلزم وجود وحدة تحكم منفصلة لذلك.

 @Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); registry.addViewController("/news").setViewName("news"); } } 

RegistrationController. هناك حاجة إلى وحدة تحكم منفصلة لصفحة التسجيل. لمعالجة طلب GET ، يتم استخدام التعليق التوضيحي GetMapping ("/ registration") ، من أجل POST -PostMapping ("/ registration") .

RegistrationController
 @Controller public class RegistrationController { @Autowired private UserService userService; @GetMapping("/registration") public String registration(Model model) { model.addAttribute("userForm", new User()); return "registration"; } @PostMapping("/registration") public String addUser(@ModelAttribute("userForm") @Valid User userForm, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { return "registration"; } if (!userForm.getPassword().equals(userForm.getPasswordConfirm())){ model.addAttribute("passwordError", "  "); return "registration"; } if (!userService.saveUser(userForm)){ model.addAttribute("usernameError", "     "); return "registration"; } return "redirect:/"; } } 


لإضافة أو الحصول على شيء من الصفحة ، ننتقل إلى النموذج. في طلب GET ، تتم إضافة كائن فارغ جديد من الفئة User إلى الصفحة. يتم ذلك من أجل عدم الحصول على البيانات من نموذج التسجيل واحدًا تلو الآخر أثناء طلب POST (اسم المستخدم وكلمة المرور وكلمة المرورالتأكيد) ، ولكن الحصول على كائن userForm المملوء على الفور.

تتوقع طريقة addUser () كمعلمة كائن المستخدم (userForm) الذي تمت إضافته أثناء طلب GET. الشرح صالح يتحقق لمعرفة ما إذا كانت القيود الموضوعة على الحقول مستوفاة ، وفي هذه الحالة يبلغ طول الحرفين على الأقل. إذا لم يتم الوفاء بالقيود ، فستحتوي عملية الربط على أخطاء.

إذا كانت كلمة المرور وتأكيدها غير متطابقين ، فأضف رسالة إلى الصفحة وأعدها. في النهاية ، نحاول حفظ إضافة المستخدم إلى قاعدة البيانات.

ترجع الطريقة saveUser () خطأ إذا كان مستخدم بنفس الاسم موجود بالفعل وصحيح إذا تم حفظ المستخدم في قاعدة البيانات. إذا فشلت محاولة الحفظ ، نضيف رسالة خطأ ونعيد الصفحة. إذا تم حفظ المستخدم بنجاح ، فانتقل إلى الصفحة الرئيسية.

AdminController. يمكن فقط للمستخدمين المشرفين الوصول إلى صفحة المسؤول. لا يوجد شيء جديد في طريقة userList () ؛ فهو يستقبل بيانات جميع المستخدمين ويضيفهم إلى الصفحة.

AdminController
 @Controller public class AdminController { @Autowired private UserService userService; @GetMapping("/admin") public String userList(Model model) { model.addAttribute("allUsers", userService.allUsers()); return "admin"; } @PostMapping("/admin") public String deleteUser(@RequestParam(required = true, defaultValue = "" ) Long userId, @RequestParam(required = true, defaultValue = "" ) String action, Model model) { if (action.equals("delete")){ userService.deleteUser(userId); } return "redirect:/admin"; } @GetMapping("/admin/gt/{userId}") public String gtUser(@PathVariable("userId") Long userId, Model model) { model.addAttribute("allUsers", userService.usergtList(userId)); return "admin"; } } 


يستخدم أسلوب deleteUser () التعليق التوضيحي RequestParam أي سيكون للعرض نموذج يجب أن يجتاز معلمتين - userId والإجراء. سيكون الرابط من النموذج http: // localhost: 8080 / admin؟ UserId = 24 & action = delete عند تنفيذ هذا الطلب ، سيتم حذف المستخدم ذي المعرف = 24.

خيار آخر لتمرير المعلمات إلى URL هو استخدام PathVariable . باستخدام هذا التعليق التوضيحي ، نحصل على الأجزاء الفردية لعنوان URL ، ولأسلوب getUser () ، سيبدو عنوان URL كما يلي: http: // localhost: 8080 / admin / gt / 24 ، وبعد النقل ، سيتم عرض قائمة بجميع المستخدمين ذوي المعرف> 24.

إعدادات الأمان

WebSecurityConfig. يحتوي على فاصولتي BCryptPasswordEncoder و AuthenticationManager ، والتي تم استيفائها مسبقًا في فئة userService.
بالإضافة إلى ذلك ، يقوم الأسلوب config () بتكوين الوصول إلى موارد الموقع المختلفة. كمعلمات للأسلوب antMatchers () ، نمرر المسارات التي نريد تعيين حد لها. ثم نوضح للمستخدمين الذين يقومون بدور هذه الصفحة / الصفحات.

WebSecurityConfig
 @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserService userService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf() .disable() .authorizeRequests() //      .antMatchers("/registration").not().fullyAuthenticated() //       .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/news").hasRole("USER") //    .antMatchers("/", "/resources/**").permitAll() //     .anyRequest().authenticated() .and() //     .formLogin() .loginPage("/login") //       .defaultSuccessUrl("/") .permitAll() .and() .logout() .permitAll() .logoutSuccessUrl("/"); } @Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder()); } } 


4.4. مضيفا وجهات النظر


index.jsp Home page ، فيما يلي خياران - للضيف وللمستخدم المخول.





index.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE HTML> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/style.css"> </head> <body> <div> <h3>${pageContext.request.userPrincipal.name}</h3> <sec:authorize access="!isAuthenticated()"> <h4><a href="/login"></a></h4> <h4><a href="/registration"></a></h4> </sec:authorize> <sec:authorize access="isAuthenticated()"> <h4><a href="/logout"></a></h4> </sec:authorize> <h4><a href="/news"> ( )</a></h4> <h4><a href="/admin"> ( )</a></h4> </div> </body> </html> 


لإخفاء جزء من المحتوى على الصفحة للمستخدمين المصرح لهم (رابط إلى صفحة التسجيل والترخيص) ، يمكنك استخدام علامة التفويض من مكتبة علامات Spring Security. تقبل معلمة الوصول العديد من التعبيرات ، على سبيل المثال ، يمكنك تعيين قيود وفقًا لدور المستخدم hasRole ('ADMIN') .

Registration.jsp صفحة التسجيل.


registration.jsp
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <form:form method="POST" modelAttribute="userForm"> <h2></h2> <div> <form:input type="text" path="username" placeholder="Username" autofocus="true"></form:input> <form:errors path="username"></form:errors> ${usernameError} </div> <div> <form:input type="password" path="password" placeholder="Password"></form:input> </div> <div> <form:input type="password" path="passwordConfirm" placeholder="Confirm your password"></form:input> <form:errors path="password"></form:errors> ${passwordError} </div> <button type="submit"></button> </form:form> <a href="/"></a> </div> </body> </html> 


في هذه الصفحة ، يتم استخدام علامة النموذج من مكتبة العلامات ، وبمساعدتها ، يتم ربط سمة طراز userForm (قمنا بإضافتها إلى الصفحة أثناء طلب GET في وحدة التحكم) والنماذج:

 <form:form method="POST" modelAttribute="userForm"> 

يجب أيضًا تحديد مسار لربط خصائص userForm:

 <form:input type="text" path="username" placeholder="Username"></form:input> 

login.jsp صفحة تسجيل الدخول .



login.jsp
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Log in with your account</title> </head> <body> <sec:authorize access="isAuthenticated()"> <% response.sendRedirect("/"); %> </sec:authorize> <div> <form method="POST" action="/login"> <h2>  </h2> <div> <input name="username" type="text" placeholder="Username" autofocus="true"/> <input name="password" type="password" placeholder="Password"/> <button type="submit">Log In</button> <h4><a href="/registration"></a></h4> </div> </form> </div> </body> </html> 


تتم معالجة هذه الصفحة ، كما سبق ذكره ، بواسطة جهاز التحكم في Spring افتراضيًا. من المهم تحديد الإجراء: action = "/ / login" واسم الإدخال.

admin.jsp صفحة المسؤول .



admin.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Log in with your account</title> <link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/style.css"> </head> <body> <div> <table> <thead> <th>ID</th> <th>UserName</th> <th>Password</th> <th>Roles</th> </thead> <c:forEach items="${allUsers}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.password}</td> <td> <c:forEach items="${user.roles}" var="role">${role.name}; </c:forEach> </td> <td> <form action="${pageContext.request.contextPath}/admin" method="post"> <input type="hidden" name="userId" value="${user.id}"/> <input type="hidden" name="action" value="delete"/> <button type="submit">Delete</button> </form> </td> </tr> </c:forEach> </table> <a href="/"></a> </div> </body> </html> 


news.jsp صفحة الأخبار ثابتة. يتم استخدامه فقط لإظهار حقوق المستخدم ، وبالتالي فإن المحتوى الذي تختاره.

news.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <h2> <br>    .</h2> <a href="/"></a> </div> </body> </html> 


5.


main Application :

 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

, , , .

spring , .

, – 3 . t_role:
SQL
 INSERT INTO public.t_role(id, name) VALUES (1, 'ROLE_USER'), (2, 'ROLE_ADMIN'); 


. -, . , - , :

SQL
 INSERT INTO public.t_user_roles(user_id, roles_id) VALUES (1, 2); 




إذا ، بعد إضافة حقوق المسؤول ، لا يمكنك الانتقال إلى صفحة المسؤول (الخطأ 403) ، انتقل إلى الموقع. فقط المستخدم الذي لديه دور المسؤول لديه حق الوصول

إلى الصفحة http: // localhost: 8080 / admin http: // localhost: 8080 / news سوف يرى أي مستخدم مسجل. يمكنك أيضًا محاولة الانتقال إلى صفحات التسجيل وتسجيل الدخول ، حيث يتم التصريح بذلك على الموقع.

استنتاج


, . . , User, Writer, .

css js , . , , Bootstrap js.

.


  1. Registration and Login with Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap and Docker Compose
  2. Spring
  3. Spring Security/ Spring Security
  4. Spring
  5. : Spring 3 MVC + Spring Security + Hibernate

Source: https://habr.com/ru/post/ar482552/


All Articles