摘要:什么是是一個能夠?yàn)榛诘钠髽I(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架。它來自于,那么它與整合開發(fā)有著天然的優(yōu)勢,目前與對應(yīng)的開源框架還有。通常大家在做一個后臺管理的系統(tǒng)的時候,應(yīng)該采用判斷用戶是否登錄。
? 什么是SpringSecurity ?
? Spring Security是一個能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉(zhuǎn)Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少了為企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。
以上來介紹來自wiki,比較官方。
? 用自己的話 簡單介紹一下,Spring Security 基于 Servlet 過濾器鏈的形式,為我們的web項(xiàng)目提供認(rèn)證與授權(quán)服務(wù)。它來自于Spring,那么它與SpringBoot整合開發(fā)有著天然的優(yōu)勢,目前與SpringSecurity對應(yīng)的開源框架還有shiro。接下來我將通過一個簡單的例子帶大家來認(rèn)識SpringSecurity,然后通過分析它的源碼帶大家來認(rèn)識一下SpringSecurity是如何工作,從一個簡單例子入門,大家由淺入深的了解學(xué)習(xí)SpringSecurity。
通常大家在做一個后臺管理的系統(tǒng)的時候,應(yīng)該采用session判斷用戶是否登錄。我記得我在沒有接觸學(xué)習(xí)SpringSecurity與shiro之前。對于用戶登錄功能實(shí)現(xiàn)通常是如下:
public String login(User user, HttpSession session){ //1、根據(jù)用戶名或者id從數(shù)據(jù)庫讀取數(shù)據(jù)庫中用戶 //2、判斷密碼是否一致 //3、如果密碼一致 session.setAttribute("user",user); //4、否則返回登錄頁面 } 對于之后那些需要登錄之后才能訪問的url,通過SpringMvc的攔截器中的#preHandle來判斷session中是否有user對象 如果沒有 則返回登錄頁面 如果有, 則允許訪問這個頁面。
但是在SpringSecurity中,這一些邏輯已經(jīng)被封裝起來,我們只需要簡單的配置一下就能使用。
接下來我通過一個簡單例子大家認(rèn)識一下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
配置一下數(shù)據(jù)庫 以及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的數(shù)據(jù)庫驅(qū)動的類的包改名了
在前面我有講過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;
對應(yīng)的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); } }
運(yùn)行測試方法,并且成功插入一條記錄。
創(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); } }
這個方法就是根據(jù)用戶名去數(shù)據(jù)庫查找用戶詳細(xì)信息。
啟動。因?yàn)槲覀冎安迦脒^一條username=yukong的記錄,所以我們查詢一下,訪問127.0.0.1:8080/user/yukong
[圖片上傳失敗...(image-ea02ac-1554981869345)]
我們可以看到 我們被重定向到了一個登錄界面,這也是我們之前引入的spring-boot-security-starter起作用了。
大家可能想問了,用戶名跟密碼是什么,用戶名默認(rèn)是user,密碼在啟動的時候已經(jīng)通過日志打印在控制臺了。
現(xiàn)在我們輸入用戶跟密碼并且登錄。就可以成功訪問我們想要訪問的接口。
從這里我們可以知道,我只需要引入了Spring-Security的依賴,它就開始生效,并且保護(hù)我們的接口了,但是現(xiàn)在有一個問題就是,它的用戶名只能是user并且密碼是通過日志打印在控制臺,但是我們希望它能通過數(shù)據(jù)來訪問我們的用戶并且判斷登錄。
其實(shí)想實(shí)現(xiàn)這個功能也很簡單。這里我們需要了解兩個接口。
UserDetails
UserDetailsService
所以,我們需要將我們的User.java實(shí)現(xiàn)這個接口
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() { // 這里我們沒有用到權(quán)限,所以返回一個默認(rèn)的admin權(quán)限 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
它只有一個方法的聲明,就是通過用戶名去查找用戶信息,從這里我們應(yīng)該知道了,SpringSecurity回調(diào)UserDetails#loadUserByUsername去獲取用戶,但是它不知道用戶信息存在哪里,所以定義成接口,讓使用者去實(shí)現(xiàn)。在我們這個項(xiàng)目用 我們的用戶是存在了數(shù)據(jù)庫中,所以我們需要調(diào)用UserMapper的方法去訪問數(shù)據(jù)庫查詢用戶信息。這里我們新建一個類叫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配置進(jìn)去
這里我們新建一個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加密我們的密碼,大家都知道密碼明文存數(shù)據(jù)庫是很不安全的。
接下里我們插入一條記錄,需要注意的是 密碼需要加密。
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); } }
接下來啟動程序,并且登錄,這次只需要輸入插入到數(shù)據(jù)中的那條記錄的用戶名跟密碼即可。
在這里一節(jié)中,我們了解到如何使用springsecurity 完成一個登錄功能,接下我們將通過分析源碼來了解為什么需要這個配置,以及SpringSecurity的工作原理是什么。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/77554.html
摘要:開公眾號差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章系列處理登錄請求前后端分離一使用完美處理權(quán)限問題前后端分離二使用完美處理權(quán)限問題前后端分離三中密碼加鹽與中異常統(tǒng)一處理 開公眾號差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章! Spring Boo...
摘要:里面配置的過濾器鏈當(dāng)用戶使用表單請求時進(jìn)入返回一個的實(shí)例一般是從數(shù)據(jù)庫中查詢出來的實(shí)例然后直接到最后一個如果有錯則拋錯給前面一個進(jìn)行拋錯如果沒有錯則放行可以訪問對應(yīng)的資源上面是總的執(zhí)行流程下面單獨(dú)說一下的認(rèn)證流程這個圖應(yīng)該都看得懂和里面的配 showImg(https://segmentfault.com/img/bVbvO0O?w=1258&h=261);web.xml里面配置的過濾...
摘要:創(chuàng)建一個工程在里面添加依賴,依賴不要隨便改我改了出錯了好幾次都找不到原因可以輕松的將對象轉(zhuǎn)換成對象和文檔同樣也可以將轉(zhuǎn)換成對象和配置 1.創(chuàng)建一個web工程2.在pom里面添加依賴,依賴不要隨便改,我改了出錯了好幾次都找不到原因 UTF-8 1.7 1.7 2.5.0 1.2 3.0-alpha-1 ...
摘要:通過上面我們知道對于表單登錄的認(rèn)證請求是交給了處理的,那么具體的認(rèn)證流程如下從上圖可知,繼承于抽象類。中維護(hù)這一個對象列表,通過遍歷判斷并且最后選擇對象來完成最后的認(rèn)證。發(fā)布一個登錄事件。 概要 前面一節(jié),通過簡單配置即可實(shí)現(xiàn)SpringSecurity表單認(rèn)證功能,而今天這一節(jié)將通過閱讀源碼的形式來學(xué)習(xí)SpringSecurity是如何實(shí)現(xiàn)這些功能, 前方高能預(yù)警,本篇分析源碼篇幅較...
閱讀 2338·2021-11-17 09:33
閱讀 862·2021-10-13 09:40
閱讀 587·2019-08-30 15:54
閱讀 792·2019-08-29 15:38
閱讀 2427·2019-08-28 18:15
閱讀 2490·2019-08-26 13:38
閱讀 1857·2019-08-26 13:36
閱讀 2142·2019-08-26 11:36