摘要:什么是是一個能夠為基于的企業(yè)應用系統提供聲明式的安全訪問控制解決方案的安全框架。它來自于,那么它與整合開發(fā)有著天然的優(yōu)勢,目前與對應的開源框架還有。通常大家在做一個后臺管理的系統的時候,應該采用判斷用戶是否登錄。
? 什么是SpringSecurity ?
? Spring Security是一個能夠為基于Spring的企業(yè)應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應用系統提供聲明式的安全訪問控制功能,減少了為企業(yè)系統安全控制編寫大量重復代碼的工作。
以上來介紹來自wiki,比較官方。
? 用自己的話 簡單介紹一下,Spring Security 基于 Servlet 過濾器鏈的形式,為我們的web項目提供認證與授權服務。它來自于Spring,那么它與SpringBoot整合開發(fā)有著天然的優(yōu)勢,目前與SpringSecurity對應的開源框架還有shiro。接下來我將通過一個簡單的例子帶大家來認識SpringSecurity,然后通過分析它的源碼帶大家來認識一下SpringSecurity是如何工作,從一個簡單例子入門,大家由淺入深的了解學習SpringSecurity。
通常大家在做一個后臺管理的系統的時候,應該采用session判斷用戶是否登錄。我記得我在沒有接觸學習SpringSecurity與shiro之前。對于用戶登錄功能實現通常是如下:
public String login(User user, HttpSession session){ //1、根據用戶名或者id從數據庫讀取數據庫中用戶 //2、判斷密碼是否一致 //3、如果密碼一致 session.setAttribute("user",user); //4、否則返回登錄頁面 } 對于之后那些需要登錄之后才能訪問的url,通過SpringMvc的攔截器中的#preHandle來判斷session中是否有user對象 如果沒有 則返回登錄頁面 如果有, 則允許訪問這個頁面。
但是在SpringSecurity中,這一些邏輯已經被封裝起來,我們只需要簡單的配置一下就能使用。
接下來我通過一個簡單例子大家認識一下SpringSecurity
本文基于SpringBoot,如果大家對SpringBoot不熟悉的話可以看看我之前寫的SpringBoot入門系列
我使用的是:
SpringBoot 2.1.4.RELEASE
SpringSecurity 5
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.4.RELEASE com.yukong springboot-springsecurity 0.0.1-SNAPSHOT springboot-springsecurity springboot-springsecurity study 1.8 org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.1 mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test org.springframework.boot spring-boot-maven-plugin
配置一下數據庫 以及MyBatis
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&characterEncoding=utf8 password: abc123 mybatis: mapper-locations: classpath:mapper/*.xml
這里我用的MySQL8.0 大家注意一下 MySQL8.0的數據庫驅動的類的包改名了
在前面我有講過SpringBoot中如何整合Mybatis,在這里我就不累述,有需要的話看這篇文章
user.sql
CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT "主鍵", `username` varchar(32) NOT NULL COMMENT "用戶名", `svc_num` varchar(32) DEFAULT NULL COMMENT "用戶號碼", `password` varchar(100) DEFAULT NULL COMMENT "密碼", `cust_id` bigint(20) DEFAULT NULL COMMENT "客戶id 1對1", PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
對應的UserMapper.java
package com.yukong.mapper; import com.yukong.entity.User; /** * * @author yukong * @date 2019-04-11 16:50 */ public interface UserMapper { int insertSelective(User record); User selectByUsername(String username); }
UserMapper.xml
id, username, svc_num, `password`, cust_id insert into user username, svc_num, `password`, cust_id, #{username,jdbcType=VARCHAR}, #{svcNum,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{custId,jdbcType=BIGINT},
在這里我們定義了兩個方法。
國際慣例ctrl+shift+t創(chuàng)建mapper的測試方法,并且插入一條記錄
package com.yukong.mapper; import com.yukong.SpringbootSpringsecurityApplicationTests; import com.yukong.entity.User; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import static org.junit.Assert.*; /** * @author yukong * @date 2019-04-11 16:53 */ public class UserMapperTest extends SpringbootSpringsecurityApplicationTests { @Autowired private UserMapper userMapper; @Test public void insert() { User user = new User(); user.setUsername("yukong"); user.setPassword("abc123"); userMapper.insertSelective(user); } }
運行測試方法,并且成功插入一條記錄。
創(chuàng)建UserController.java
package com.yukong.controller; import com.yukong.entity.User; import com.yukong.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author yukong * @date 2019-04-11 15:22 */ @RestController public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("/user/{username}") public User hello(@PathVariable String username) { return userMapper.selectByUsername(username); } }
這個方法就是根據用戶名去數據庫查找用戶詳細信息。
啟動。因為我們之前插入過一條username=yukong的記錄,所以我們查詢一下,訪問127.0.0.1:8080/user/yukong
[圖片上傳失敗...(image-ea02ac-1554981869345)]
我們可以看到 我們被重定向到了一個登錄界面,這也是我們之前引入的spring-boot-security-starter起作用了。
大家可能想問了,用戶名跟密碼是什么,用戶名默認是user,密碼在啟動的時候已經通過日志打印在控制臺了。
現在我們輸入用戶跟密碼并且登錄。就可以成功訪問我們想要訪問的接口。
從這里我們可以知道,我只需要引入了Spring-Security的依賴,它就開始生效,并且保護我們的接口了,但是現在有一個問題就是,它的用戶名只能是user并且密碼是通過日志打印在控制臺,但是我們希望它能通過數據來訪問我們的用戶并且判斷登錄。
其實想實現這個功能也很簡單。這里我們需要了解兩個接口。
UserDetails
UserDetailsService
所以,我們需要將我們的User.java實現這個接口
package com.yukong.entity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; /** * * @author yukong * @date 2019-04-11 16:50 */ public class User implements UserDetails { /** * 主鍵 */ private Long id; /** * 用戶名 */ private String username; /** * 用戶號碼 */ private String svcNum; /** * 密碼 */ private String password; /** * 客戶id 1對1 */ private Long custId; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return false; } @Override public boolean isAccountNonLocked() { return false; } @Override public boolean isCredentialsNonExpired() { return false; } @Override public boolean isEnabled() { return false; } public void setUsername(String username) { this.username = username; } public String getSvcNum() { return svcNum; } public void setSvcNum(String svcNum) { this.svcNum = svcNum; } @Override public Collection extends GrantedAuthority> getAuthorities() { // 這里我們沒有用到權限,所以返回一個默認的admin權限 return AuthorityUtils.commaSeparatedStringToAuthorityList("admin"); } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Long getCustId() { return custId; } public void setCustId(Long custId) { this.custId = custId; } }
接下來我們再看看UserDetailsService
它只有一個方法的聲明,就是通過用戶名去查找用戶信息,從這里我們應該知道了,SpringSecurity回調UserDetails#loadUserByUsername去獲取用戶,但是它不知道用戶信息存在哪里,所以定義成接口,讓使用者去實現。在我們這個項目用 我們的用戶是存在了數據庫中,所以我們需要調用UserMapper的方法去訪問數據庫查詢用戶信息。這里我們新建一個類叫MyUserDetailsServiceImpl
package com.yukong.config; import com.yukong.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** * @author yukong * @date 2019-04-11 17:35 */ @Service public class MyUserDetailServiceImpl implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userMapper.selectByUsername(username); } }
然后新建一個類去把我們的UserDetailsService配置進去
這里我們新建一個SecurityConfig
package com.yukong.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @author yukong * @date 2019-04-11 15:08 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // 配置UserDetailsService 跟 PasswordEncoder 加密器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); auth.eraseCredentials(false); } }
在這里我們還配置了一個PasswordEncoder加密我們的密碼,大家都知道密碼明文存數據庫是很不安全的。
接下里我們插入一條記錄,需要注意的是 密碼需要加密。
package com.yukong.mapper; import com.yukong.SpringbootSpringsecurityApplicationTests; import com.yukong.entity.User; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import static org.junit.Assert.*; /** * @author yukong * @date 2019-04-11 16:53 */ public class UserMapperTest extends SpringbootSpringsecurityApplicationTests { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserMapper userMapper; @Test public void insert() { User user = new User(); user.setUsername("yukong"); user.setPassword(passwordEncoder.encode("abc123")); userMapper.insertSelective(user); } }
接下來啟動程序,并且登錄,這次只需要輸入插入到數據中的那條記錄的用戶名跟密碼即可。
在這里一節(jié)中,我們了解到如何使用springsecurity 完成一個登錄功能,接下我們將通過分析源碼來了解為什么需要這個配置,以及SpringSecurity的工作原理是什么。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77554.html
摘要:開公眾號差不多兩年了,有不少原創(chuàng)教程,當原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章系列處理登錄請求前后端分離一使用完美處理權限問題前后端分離二使用完美處理權限問題前后端分離三中密碼加鹽與中異常統一處理 開公眾號差不多兩年了,有不少原創(chuàng)教程,當原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章! Spring Boo...
摘要:里面配置的過濾器鏈當用戶使用表單請求時進入返回一個的實例一般是從數據庫中查詢出來的實例然后直接到最后一個如果有錯則拋錯給前面一個進行拋錯如果沒有錯則放行可以訪問對應的資源上面是總的執(zhí)行流程下面單獨說一下的認證流程這個圖應該都看得懂和里面的配 showImg(https://segmentfault.com/img/bVbvO0O?w=1258&h=261);web.xml里面配置的過濾...
摘要:創(chuàng)建一個工程在里面添加依賴,依賴不要隨便改我改了出錯了好幾次都找不到原因可以輕松的將對象轉換成對象和文檔同樣也可以將轉換成對象和配置 1.創(chuàng)建一個web工程2.在pom里面添加依賴,依賴不要隨便改,我改了出錯了好幾次都找不到原因 UTF-8 1.7 1.7 2.5.0 1.2 3.0-alpha-1 ...
摘要:通過上面我們知道對于表單登錄的認證請求是交給了處理的,那么具體的認證流程如下從上圖可知,繼承于抽象類。中維護這一個對象列表,通過遍歷判斷并且最后選擇對象來完成最后的認證。發(fā)布一個登錄事件。 概要 前面一節(jié),通過簡單配置即可實現SpringSecurity表單認證功能,而今天這一節(jié)將通過閱讀源碼的形式來學習SpringSecurity是如何實現這些功能, 前方高能預警,本篇分析源碼篇幅較...
閱讀 2312·2021-11-17 09:33
閱讀 843·2021-10-13 09:40
閱讀 573·2019-08-30 15:54
閱讀 778·2019-08-29 15:38
閱讀 2417·2019-08-28 18:15
閱讀 2475·2019-08-26 13:38
閱讀 1841·2019-08-26 13:36
閱讀 2129·2019-08-26 11:36