SpringSecurity數(shù)據(jù)庫(kù)進(jìn)行認(rèn)證和授權(quán)的使用
在前面的文章中,我們介紹了Spring Security基于內(nèi)存的一些基本使用方法,但在真實(shí)的業(yè)務(wù)場(chǎng)景中,用戶(hù)的賬號(hào)、密碼以及角色信息肯定都是存放在數(shù)據(jù)庫(kù)中的,所以我們需要從數(shù)據(jù)庫(kù)中來(lái)加載認(rèn)證和授權(quán)的數(shù)據(jù)。
一、準(zhǔn)備工作
如下案例是基于上一篇中的案例改造而來(lái),所以建議先閱讀前一篇文章的內(nèi)容,將基本案例的代碼準(zhǔn)備好。
1.1 導(dǎo)入相關(guān)依賴(lài)
除了引入security的依賴(lài)外,我們還需要引入數(shù)據(jù)庫(kù)驅(qū)動(dòng)、連接池、MyBatis的依賴(lài)包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- security使用數(shù)據(jù)庫(kù)做驗(yàn)證需要增加如下三個(gè)依賴(lài) --> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency>
1.2 配置信息
我們需要在配置文件中增加數(shù)據(jù)源的配置信息,指定mapper xml的位置,這里改成你自己的位置即可。
# 配置數(shù)據(jù)源信息 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://x.x.x.x:3306/zhangxun?characterEncoding=utf-8&&serverTimezone=Asia/Shanghai&&useSSL=false spring.datasource.username=root spring.datasource.password=root # 配置mapper xml的位置 mybatis.mapper-locations=classpath:mapper/*Mapper.xml # 是否顯示執(zhí)行sql logging.level.com.xun.mapper=debug
1.3 數(shù)據(jù)庫(kù)準(zhǔn)備
我們需要?jiǎng)?chuàng)建如下三張表,分別用來(lái)存儲(chǔ)用戶(hù)信息、角色信息、用戶(hù)與角色的對(duì)應(yīng)關(guān)系。
CREATE TABLE `auth_user` ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `user_name` varchar(100) DEFAULT NULL, `password` varchar(100) DEFAULT NULL, `expired` tinyint(1) DEFAULT NULL, `locked` tinyint(1) DEFAULT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 -- 密文密碼的生成邏輯下面1.8節(jié)會(huì)講到,可以替換為自己密碼的密文進(jìn)行插入 INSERT INTO zhangxun.auth_user (user_id, user_name, password, expired, locked) VALUES(1, 'root', '$2a$10$Hv037iGDdj82YjFORlYnyOdlra2EObV2XdddyW8A.r.Ph5ETOBNo2', 0, 0); INSERT INTO zhangxun.auth_user (user_id, user_name, password, expired, locked) VALUES(2, 'zhang', '$2a$10$cVgFLGx0Crz0Jf2TDBhJ.e6FS.BpH7YOoox2iSJrGW1DJ6OXiOt86', 0, 0); CREATE TABLE `auth_role` ( `role_id` int(11) NOT NULL AUTO_INCREMENT, `role_code` varchar(100) DEFAULT NULL, `role_name` varchar(100) DEFAULT NULL, PRIMARY KEY (`role_id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; INSERT INTO zhangxun.auth_role (role_id, role_code, role_name) VALUES(1, 'admin', '管理員'); INSERT INTO zhangxun.auth_role (role_id, role_code, role_name) VALUES(2, 'manager', '經(jīng)理'); CREATE TABLE `auth_user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) DEFAULT NULL, `role_code` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; -- root用戶(hù)將擁有admin和manager兩個(gè)角色,zhang用戶(hù)僅擁有manager一個(gè)角色 INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(1, 1, 'admin'); INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(2, 1, 'manager'); INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(3, 2, 'manager');
1.4 實(shí)體類(lèi)的創(chuàng)建
@Data public class Role { private Integer roleId; private String roleCode; private String roleName; } @Data public class User implements UserDetails { private Integer userId; private String userName; private String password; private Integer expired; private Integer locked; private List<Role> roles; /** * 獲取用戶(hù)的所有角色信息 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for(Role role : roles){ // 也可以在數(shù)據(jù)中添加角色時(shí),就以 ROLE_ 開(kāi)始,這樣就不用二次添加了 authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode())); } return authorities; } /** * 指定哪一個(gè)是用戶(hù)的密碼字段 * @return */ @Override public String getPassword() { return password; } /** * 指定哪一個(gè)是用戶(hù)的賬戶(hù)字段 * @return */ @Override public String getUsername() { return userName; } /** * 判斷賬戶(hù)是否過(guò)期 * @return */ @Override public boolean isAccountNonExpired() { return (expired == 0); } /** * 判斷賬戶(hù)是否鎖定 * @return */ @Override public boolean isAccountNonLocked() { return (locked == 0); } /** * 判斷密碼是否過(guò)期 * 可以根據(jù)業(yè)務(wù)邏輯或者數(shù)據(jù)庫(kù)字段來(lái)決定 * @return */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 判斷賬戶(hù)是否可用 * 可以根據(jù)業(yè)務(wù)邏輯或者數(shù)據(jù)庫(kù)字段來(lái)決定 * @return */ @Override public boolean isEnabled() { return true; } }
實(shí)現(xiàn)UserDetails接口的實(shí)體類(lèi)會(huì)被認(rèn)為是User實(shí)體,Spring Security會(huì)根據(jù)重寫(xiě)的方法來(lái)加載用戶(hù)的必要信息:賬戶(hù)信息、密碼信息、賬戶(hù)過(guò)期、賬戶(hù)鎖定、密碼過(guò)期、賬戶(hù)啟用、賬戶(hù)擁有的角色信息。
1.5 Dao層的創(chuàng)建
@Mapper public interface UserMapper { User getUserByUserName(String userName); List<Role> getUserRolesByUserId(Integer userId); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.securitydemo.mapper.UserMapper"> <select id="getUserByUserName" parameterType="string" resultType="com.example.securitydemo.po.User"> select au.user_id userId, au.user_name userName, au.password, au.expired, au.locked from auth_user au where au.user_name = #{userName} </select> <select id="getUserRolesByUserId" parameterType="integer" resultType="com.example.securitydemo.po.Role"> select ar.role_id roleId, ar.role_code roleCode, ar.role_name roleName from auth_user_role aur left join auth_role ar on aur.role_code = ar.role_code where aur.user_id = #{userId} </select </mapper>
注意,如果UserMapper接口你是用了@Repository注解,那么就需要在啟動(dòng)類(lèi)上加上Mapper所在包的位置。
@MapperScan("com.example.securitydemo.mapper")
1.6 Service層的編寫(xiě)
@Slf4j @Service public class UserService implements UserDetailsService { @Resource private UserMapper userMapper; /** * 根據(jù)用戶(hù)名去數(shù)據(jù)庫(kù)獲取用戶(hù)信息,SpringSecutity會(huì)自動(dòng)進(jìn)行密碼的比對(duì) * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 用戶(hù)名必須是唯一的,不允許重復(fù) User user = userMapper.getUserByUserName(username); if(ObjectUtils.isEmpty(user)){ throw new UsernameNotFoundException("根據(jù)用戶(hù)名找不到該用戶(hù)的信息!"); } List<Role> roleList = userMapper.getUserRolesByUserId(user.getUserId()); user.setRoles(roleList); return user; } }
1.7 Security配置
@EnableWebSecurity public class DBSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; /** * 對(duì)請(qǐng)求進(jìn)行鑒權(quán)的配置 * * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 任何角色允許訪問(wèn) .antMatchers("/", "/index").permitAll() // 僅admin角色可以訪問(wèn) .antMatchers("/admin/**").hasRole("admin") // admin和manager兩個(gè)角色可以訪問(wèn) .antMatchers("/manager/**").hasAnyRole("admin", "manager") .and() // 沒(méi)有權(quán)限進(jìn)入內(nèi)置的登錄頁(yè)面 .formLogin() .and() // 暫時(shí)關(guān)閉CSRF校驗(yàn),允許get請(qǐng)求登出 .csrf().disable(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService); } /** * 默認(rèn)開(kāi)啟密碼加密,前端傳入的密碼Security會(huì)在加密后和數(shù)據(jù)庫(kù)中的密文進(jìn)行比對(duì),一致的話就登錄成功 * 所以必須提供一個(gè)加密對(duì)象,供security加密前端明文密碼使用 * @return */ @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
1.8 密碼加密
@Slf4j public class PasswordEncode { private PasswordEncoder passwordEncoder; @Before public void before(){ this.passwordEncoder = new BCryptPasswordEncoder(); } @Test public void encodePassword(){ String rawPassword = "mm000"; String encodePassword = passwordEncoder.encode(rawPassword); log.info("password:{} encoded is: {}", rawPassword, encodePassword); } }
該類(lèi)是一個(gè)測(cè)試類(lèi),為了將明文密碼加密后得到密文的,密文存儲(chǔ)到User表的password字段。Spring Security默認(rèn)開(kāi)啟密碼加密功能的,數(shù)據(jù)庫(kù)加載出來(lái)的密碼會(huì)被進(jìn)行格式校驗(yàn),如果不是合法的密文,登錄邏輯就會(huì)失敗。
1.9 測(cè)試結(jié)果
訪問(wèn)localhost:8080/index無(wú)需登錄,直接就能返回結(jié)果。
訪問(wèn)localhost:8080/admin/getHello將跳轉(zhuǎn)到登錄頁(yè)面,使用root賬戶(hù)(包含admin和manager兩個(gè)角色),隨便輸入一個(gè)密碼,登錄失?。惠斎胝_的密碼,登錄成功后返回正確的內(nèi)容;此時(shí)再訪問(wèn)localhost:8080/manager/getHello也能獲得正確的內(nèi)容。
調(diào)用localhost:8080/logout后將退出登錄,此時(shí)使用zhang賬戶(hù)(僅包含manager角色),輸入正確的密碼,登錄成功后返回正確的內(nèi)容;此時(shí)再訪問(wèn)localhost:8080/admin/getHello將無(wú)法獲得內(nèi)容。
到此這篇關(guān)于SpringSecurity數(shù)據(jù)庫(kù)進(jìn)行認(rèn)證和授權(quán)的使用的文章就介紹到這了,更多相關(guān)SpringSecurity 數(shù)據(jù)庫(kù)認(rèn)證和授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity實(shí)現(xiàn)權(quán)限認(rèn)證與授權(quán)的使用示例
- SpringSecurity進(jìn)行認(rèn)證與授權(quán)的示例代碼
- springSecurity用戶(hù)認(rèn)證和授權(quán)的實(shí)現(xiàn)
- SpringBoot整合SpringSecurity認(rèn)證與授權(quán)
- 深入淺析springsecurity入門(mén)登錄授權(quán)
- SpringSecurityOAuth2實(shí)現(xiàn)微信授權(quán)登錄
- SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證
- springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐
- SpringSecurity授權(quán)機(jī)制的實(shí)現(xiàn)(AccessDecisionManager與投票決策)
相關(guān)文章
Spring深入講解實(shí)現(xiàn)AOP的三種方式
Spring的AOP就是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,使用了兩個(gè)動(dòng)態(tài)代理,分別是JDK的動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理,本文重點(diǎn)給大家介紹下Spring?Aop的三種實(shí)現(xiàn),感興趣的朋友一起看看吧2022-05-05SpringBoot自定義注解使用讀寫(xiě)分離Mysql數(shù)據(jù)庫(kù)的實(shí)例教程
這篇文章主要給大家介紹了關(guān)于SpringBoot自定義注解使用讀寫(xiě)分離Mysql數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Java爬蟲(chóng)實(shí)現(xiàn)爬取京東上的手機(jī)搜索頁(yè)面 HttpCliient+Jsoup
下面小編就為大家分享一篇Java爬蟲(chóng)實(shí)現(xiàn)爬取京東上的手機(jī)搜索頁(yè)面 HttpCliient+Jsoup,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Java中雙冒號(hào)運(yùn)算符(::)的用法詳解
在Java 8引入的Lambda表達(dá)式和函數(shù)式接口之后,雙冒號(hào)運(yùn)算符(::)成為了一項(xiàng)重要的功能,下面我們就來(lái)學(xué)習(xí)一下Java中的雙冒號(hào)運(yùn)算符及其常見(jiàn)應(yīng)用場(chǎng)景吧2023-12-12java實(shí)現(xiàn)系統(tǒng)托盤(pán)示例
桌面的系統(tǒng)托盤(pán)即當(dāng)程序最小化或者關(guān)閉按鈕程序并沒(méi)有退出,而是最小化在任務(wù)狀態(tài)區(qū)域,下面是使用java實(shí)現(xiàn)系統(tǒng)托盤(pán)示例2014-03-03Java實(shí)現(xiàn)IP地址到二進(jìn)制的轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)IP地址到二進(jìn)制的轉(zhuǎn)換,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11關(guān)于Lombok簡(jiǎn)化編碼使用及說(shuō)明
這篇文章主要介紹了關(guān)于Lombok簡(jiǎn)化編碼使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08