日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Spring Security UserDetails實現原理詳解

瀏覽:26日期:2023-08-15 17:38:43

1. 前言

今天開始我們來一步步窺探它是如何工作的。我們又該如何駕馭它。本篇將通過 Spring Boot 2.x 來講解 Spring Security 中的用戶主體UserDetails。以及從中找點樂子。

2. Spring Boot 集成 Spring Security

這個簡直老生常談了。不過為了照顧大多數還是說一下。集成 Spring Security 只需要引入其對應的 Starter 組件。Spring Security 不僅僅能保護Servlet Web 應用,也可以保護Reactive Web應用,本文我們講前者。我們只需要在 Spring Security 項目引入以下依賴即可:

<dependencies> <!-- actuator 指標監控 非必須 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- spring security starter 必須 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- spring mvc servlet web 必須 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- lombok 插件 非必須 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 測試 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>

3. UserDetailsServiceAutoConfiguration

啟動項目,訪問Actuator端點http://localhost:8080/actuator會跳轉到一個登錄頁面http://localhost:8080/login如下:

Spring Security UserDetails實現原理詳解

要求你輸入用戶名 Username (默認值為user)和密碼 Password 。密碼在springboot控制臺會打印出類似 Using generated security password: e1f163be-ad18-4be1-977c-88a6bcee0d37 的字樣,后面的長串就是密碼,當然這不是生產可用的。如果你足夠細心會從控制臺打印日志發現該隨機密碼是由UserDetailsServiceAutoConfiguration 配置類生成的,我們就從它開始順藤摸瓜來一探究竟。

3.1 UserDetailsService

UserDetailsService接口。該接口只提供了一個方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

該方法很容易理解:通過用戶名來加載用戶 。這個方法主要用于從系統數據中查詢并加載具體的用戶到Spring Security中。

3.2 UserDetails

從上面UserDetailsService 可以知道最終交給Spring Security的是UserDetails 。該接口是提供用戶信息的核心接口。該接口實現僅僅存儲用戶的信息。后續會將該接口提供的用戶信息封裝到認證對象Authentication中去。UserDetails 默認提供了:

用戶的權限集, 默認需要添加ROLE_ 前綴 用戶的加密后的密碼, 不加密會使用{noop}前綴 應用內唯一的用戶名 賬戶是否過期 賬戶是否鎖定 憑證是否過期 用戶是否可用

如果以上的信息滿足不了你使用,你可以自行實現擴展以存儲更多的用戶信息。比如用戶的郵箱、手機號等等。通常我們使用其實現類:

org.springframework.security.core.userdetails.User

該類內置一個建造器UserBuilder 會很方便地幫助我們構建UserDetails 對象,后面我們會用到它。

3.3 UserDetailsServiceAutoConfiguration

UserDetailsServiceAutoConfiguration 全限定名為:

org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration

源碼如下:

@Configuration@ConditionalOnClass(AuthenticationManager.class)@ConditionalOnBean(ObjectPostProcessor.class)@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class })public class UserDetailsServiceAutoConfiguration { private static final String NOOP_PASSWORD_PREFIX = '{noop}'; private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile('^{.+}.*$'); private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class); @Bean @ConditionalOnMissingBean( type = 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository') @Lazy public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder){ SecurityProperties.User user = properties.getUser(); List<String> roles = user.getRoles(); return new InMemoryUserDetailsManager(User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())) .roles(StringUtils.toStringArray(roles)).build()); } private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) { String password = user.getPassword(); if (user.isPasswordGenerated()) { logger.info(String.format('%n%nUsing generated security password: %s%n', user.getPassword())); } if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) { return password; } return NOOP_PASSWORD_PREFIX + password; }}

我們來簡單解讀一下該類,從@Conditional系列注解我們知道該類在類路徑下存在AuthenticationManager、在Spring 容器中存在Bean ObjectPostProcessor并且不存在Bean AuthenticationManager, AuthenticationProvider, UserDetailsService的情況下生效。千萬不要糾結這些類干嘛用的! 該類只初始化了一個UserDetailsManager 類型的Bean。UserDetailsManager 類型負責對安全用戶實體抽象UserDetails的增刪查改操作。同時還繼承了UserDetailsService接口。

明白了上面這些讓我們把目光再回到UserDetailsServiceAutoConfiguration 上來。該類初始化了一個名為InMemoryUserDetailsManager 的內存用戶管理器。該管理器通過配置注入了一個默認的UserDetails存在內存中,就是我們上面用的那個user ,每次啟動user都是動態生成的。那么問題來了如果我們定義自己的UserDetailsManager Bean是不是就可以實現我們需要的用戶管理邏輯呢?

3.4 自定義UserDetailsManager

我們來自定義一個UserDetailsManager 來看看能不能達到自定義用戶管理的效果。首先我們針對UserDetailsManager 的所有方法進行一個代理的實現,我們依然將用戶存在內存中,區別就是這是我們自定義的:

package cn.felord.spring.security;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UsernameNotFoundException;import java.util.HashMap;import java.util.Map;/** * 代理 {@link org.springframework.security.provisioning.UserDetailsManager} 所有功能 * * @author Felordcn */public class UserDetailsRepository { private Map<String, UserDetails> users = new HashMap<>(); public void createUser(UserDetails user) { users.putIfAbsent(user.getUsername(), user); } public void updateUser(UserDetails user) { users.put(user.getUsername(), user); } public void deleteUser(String username) { users.remove(username); } public void changePassword(String oldPassword, String newPassword) { Authentication currentUser = SecurityContextHolder.getContext().getAuthentication(); if (currentUser == null) { // This would indicate bad coding somewhere throw new AccessDeniedException( 'Can’t change password as no Authentication object found in context ' + 'for current user.'); } String username = currentUser.getName(); UserDetails user = users.get(username); if (user == null) { throw new IllegalStateException('Current user doesn’t exist in database.'); } // todo copy InMemoryUserDetailsManager 自行實現具體的更新密碼邏輯 } public boolean userExists(String username) { return users.containsKey(username); } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return users.get(username); }}

該類負責具體對UserDetails 的增刪改查操作。我們將其注入Spring 容器:

@Bean public UserDetailsRepository userDetailsRepository() { UserDetailsRepository userDetailsRepository = new UserDetailsRepository(); // 為了讓我們的登錄能夠運行 這里我們初始化一個用戶Felordcn 密碼采用明文 當你在密碼12345上使用了前綴{noop} 意味著你的密碼不使用加密,authorities 一定不能為空 這代表用戶的角色權限集合 UserDetails felordcn = User.withUsername('Felordcn').password('{noop}12345').authorities(AuthorityUtils.NO_AUTHORITIES).build(); userDetailsRepository.createUser(felordcn); return userDetailsRepository; }

為了方便測試 我們也內置一個名稱為Felordcn 密碼為12345的UserDetails用戶,密碼采用明文 當你在密碼12345上使用了前綴{noop} 意味著你的密碼不使用加密,這里我們并沒有指定密碼加密方式你可以使用PasswordEncoder 來指定一種加密方式。通常推薦使用Bcrypt作為加密方式。默認Spring Security使用的也是此方式。authorities 一定不能為null 這代表用戶的角色權限集合。接下來我們實現一個UserDetailsManager 并注入Spring 容器:

@Bean public UserDetailsManager userDetailsManager(UserDetailsRepository userDetailsRepository) { return new UserDetailsManager() { @Override public void createUser(UserDetails user) {userDetailsRepository.createUser(user); } @Override public void updateUser(UserDetails user) {userDetailsRepository.updateUser(user); } @Override public void deleteUser(String username) {userDetailsRepository.deleteUser(username); } @Override public void changePassword(String oldPassword, String newPassword) {userDetailsRepository.changePassword(oldPassword, newPassword); } @Override public boolean userExists(String username) {return userDetailsRepository.userExists(username); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userDetailsRepository.loadUserByUsername(username); } }; }

這樣實際執行委托給了UserDetailsRepository 來做。我們重復 章節3. 的動作進入登陸頁面分別輸入Felordcn和12345 成功進入。

3.5 數據庫管理用戶

經過以上的配置,相信聰明的你已經知道如何使用數據庫來管理用戶了 。只需要將 UserDetailsRepository 中的 users 屬性替代為抽象的Dao接口就行了,無論你使用Jpa還是Mybatis來實現。

4. 總結

今天我們對Spring Security 中的用戶信息 UserDetails 相關進行的一些解讀。并自定義了用戶信息處理服務。相信你已經對在Spring Security中如何加載用戶信息,如何擴展用戶信息有所掌握了。后面我們會由淺入深慢慢解讀Spring Security。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
精品理论电影在线| 国产日韩三级| 国产日韩免费| 日本99精品| 久久国内精品自在自线400部| 日本少妇一区二区| 日韩av一级片| 亚洲一区欧美激情| 亚洲综合精品| 亚洲欧美一级| 日韩国产一区二区| 午夜精品影视国产一区在线麻豆| 欧美成人日韩| 91亚洲一区| 日韩理论片av| 亚洲一区二区三区高清| 日韩av一二三| 天堂av在线| 最新国产精品久久久| 国际精品欧美精品| 老司机精品久久| 免费看欧美美女黄的网站| 99视频一区| 免费福利视频一区二区三区| 亚洲精品电影| 国产乱码精品一区二区三区四区| 999国产精品视频| 国产欧美日韩在线一区二区| 美女精品网站| 亚洲精品.com| 国产精品一区二区精品| 国产亚洲亚洲| 日韩精品91| 日韩av网站在线免费观看| 91九色精品国产一区二区| 国产专区精品| 日本韩国欧美超级黄在线观看| 天堂俺去俺来也www久久婷婷| 美女国产一区| 国产麻豆一区二区三区| 人人草在线视频| 999久久久91| 国产日产精品_国产精品毛片| 91精品xxx在线观看| 91精品国产成人观看| 国产精品xxx| 免费在线播放第一区高清av| 欧美国产中文高清| 岛国av免费在线观看| 久久久久伊人| 97国产精品| 国产亚洲一区| 欧美va天堂在线| 久久精品国产一区二区| 日韩av网站在线免费观看| 免费在线看一区| 日本成人在线视频网站| 嫩草伊人久久精品少妇av杨幂| yellow在线观看网址| 日本中文字幕不卡| 午夜久久美女| 精品欧美视频| 成人av二区| 亚洲一区亚洲| 国产成人精品亚洲线观看| 中文久久精品| 成人羞羞在线观看网站| 日韩精品中文字幕吗一区二区 | 婷婷综合在线| 日韩一区二区三区高清在线观看| 国产亚洲一区二区手机在线观看 | 亚洲一区久久| 不卡一二三区| 久久一区欧美| 国产精品videossex久久发布| 蜜桃久久久久| 日韩大片免费观看| 天堂网av成人| 中文av在线全新| 最新中文字幕在线播放| 亚洲啊v在线| 欧美成人基地| 激情婷婷综合| 激情欧美一区二区三区| 一区二区电影在线观看| 国产欧美日韩视频在线| 国产91一区| 婷婷亚洲成人| 黄色网一区二区| 丝袜美腿一区二区三区| 日韩av不卡一区二区| 亚洲成a人片| 欧美日韩亚洲一区三区| 91精品国产调教在线观看| 黄色亚洲精品| 欧美中文高清| 欧美高清一区| 国产精品毛片久久久| 久久精品国产99久久| 亚洲欧洲美洲国产香蕉| av高清不卡| 99久久夜色精品国产亚洲1000部| 日本伊人久久| 欧美好骚综合网| 日韩一区二区三区四区五区| 国产美女高潮在线观看| 亚洲日韩中文字幕一区| 免费不卡中文字幕在线| 国产高清精品二区| 美女在线视频一区| 午夜精品影视国产一区在线麻豆| 欧美日韩精品免费观看视欧美高清免费大片 | 91精品在线免费视频| 午夜久久tv| 在线人成日本视频| 麻豆91小视频| 日韩在线观看中文字幕| 久久久久99| 美美哒免费高清在线观看视频一区二区| 成人黄色av| 国产91欧美| 欧美激情日韩| 日韩精品一区二区三区中文字幕| 国产精选在线| 捆绑调教日本一区二区三区| 国产午夜久久av| 欧美视频精品全部免费观看| 日韩av午夜在线观看| 日韩精品三区四区| 日本综合精品一区| 日韩福利在线观看| 国产精品www994| 午夜av不卡| 伊人精品在线| 一区二区三区四区在线观看国产日韩| 中文无码久久精品| 国产视频一区二| 日韩影片在线观看| 欧美日韩xxxx| 国语对白精品一区二区| 日韩中文欧美| 中文日韩欧美| 日韩精品免费视频一区二区三区| 国产极品模特精品一二| 日韩欧美另类一区二区| 男人的天堂亚洲一区| 久久国际精品| 中文字幕在线视频网站| 婷婷亚洲综合| 久久激情综合网| 超碰超碰人人人人精品| 蜜桃国内精品久久久久软件9| 香蕉国产精品| 欧美日韩一区自拍| 91视频一区| 亚洲我射av| 综合日韩av| 国产精品中文| 99国产精品久久久久久久| 国产日韩视频在线| 黄色av日韩| 国产精品v一区二区三区| 美国欧美日韩国产在线播放| 麻豆成全视频免费观看在线看| 国产成人黄色| 涩涩av在线| 综合国产在线| 国产精品tv| 国产亚洲高清视频| 麻豆精品在线观看| 亚洲资源网站| 欧美福利专区| 午夜影院一区| 美女视频一区在线观看| 老鸭窝毛片一区二区三区| 国产精品一区二区av交换| 国产精品婷婷| 136国产福利精品导航网址| 欧美1区2区3| 少妇精品久久久一区二区| 婷婷激情久久| 激情婷婷欧美| 精品一区二区三区视频在线播放 | 精品国产乱码| 综合五月婷婷| 好看不卡的中文字幕| 在线亚洲人成| 亚洲日本网址| 丝袜诱惑一区二区| 六月丁香综合在线视频| 中文字幕免费一区二区| 中文欧美日韩| 婷婷成人综合| 免费黄色成人| 狠狠爱www人成狠狠爱综合网| 日韩午夜免费| 伊人久久大香伊蕉在人线观看热v| 激情欧美一区二区三区| 91成人精品| 亚洲欧美久久精品|