SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證
(一)概述
Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問(wèn)控制框架,Spring Security主要做兩個(gè)事情,認(rèn)證、授權(quán)。我之前寫過(guò)一篇關(guān)于SpringSecurity的博客,但是當(dāng)時(shí)只是介紹了基于mock數(shù)據(jù)的案例,本期就來(lái)介紹一下基于真實(shí)數(shù)據(jù)的認(rèn)證授權(quán)實(shí)現(xiàn)。
(二)前期項(xiàng)目搭建
為了更好的展示SpringSecurity,我們先搭建一個(gè)簡(jiǎn)單的web項(xiàng)目出來(lái)。引入thymeleaf依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency>
新建一個(gè)登陸頁(yè),一個(gè)首頁(yè),然后幾個(gè)不同等級(jí)的展示頁(yè)面:
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸頁(yè)</title> </head> <body> <div> <form> <h2>登陸頁(yè)</h2> <input type="text" id="username" placeholder="username"> <input type="password" id="password" placeholder="password"> <button type="button">登陸</button> </form> </div> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首頁(yè)</title> </head> <body> <div> <h2>首頁(yè)</h2> <a href="/login" rel="external nofollow" >登陸</a> <div style="overflow: hidden"> <div style="float: left;margin-left: 20px"> <h3>level1</h3> <a href="/level1/1" rel="external nofollow" >level-1-1</a> <hr> <a href="/level1/2" rel="external nofollow" >level-1-2</a> </div> <div style="float: left;margin-left: 20px"> <h3>level2</h3> <a href="/level2/1" rel="external nofollow" >level-2-1</a> <hr> <a href="/level2/2" rel="external nofollow" >level-2-2</a> </div> <div style="float: left;margin-left: 20px"> <h3>level3</h3> <a href="/level3/1" rel="external nofollow" >level-3-1</a> <hr> <a href="/level3/2" rel="external nofollow" >level-3-2</a> </div> </div> </div> </body> </html>
另外還有幾個(gè)不同等級(jí)的頁(yè)面
分別在body中寫上自己對(duì)應(yīng)的編號(hào)。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> level-1-1 </body> </html>
最后編寫一個(gè)controller來(lái)接收請(qǐng)求:
@Controller public class RouteController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/login") public String toLogin(){ return "login"; } @RequestMapping("/level1/{id}") public String level1(@PathVariable("id")String id){ return "level1/"+id; } @RequestMapping("/level2/{id}") public String level2(@PathVariable("id")String id){ return "level2/"+id; } @RequestMapping("/level3/{id}") public String level3(@PathVariable("id")String id){ return "level3/"+id; } }
最終的效果如下:
最終實(shí)現(xiàn)等級(jí)不同的level頁(yè)面根據(jù)不同權(quán)限進(jìn)行跳轉(zhuǎn)。
后臺(tái)基于Mybatis和Mysql數(shù)據(jù)庫(kù)實(shí)現(xiàn),因此我們除了引入SpringSecurity的依賴之外,還需要引入Mybatis相關(guān)依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency>
在配置文件中添加數(shù)據(jù)源相關(guān)信息,以及Mybatis的配置:
spring.datasource.url=jdbc:mysql://localhost:3306/security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver mybatis.mapper-locations=classpath:mapper/*.xml
(三)認(rèn)證與授權(quán)的實(shí)現(xiàn)
3.1 表結(jié)構(gòu)設(shè)計(jì)
認(rèn)證和授權(quán)在表設(shè)計(jì)上應(yīng)該分在兩個(gè)表內(nèi),一個(gè)表存儲(chǔ)用戶信息包括密碼等,另一個(gè)表存儲(chǔ)授權(quán)信息,還需要一個(gè)表建立用戶和授權(quán)之間的關(guān)聯(lián),給出最終的表結(jié)構(gòu):
CREATE TABLE `roles` ( `id` int(4) NOT NULL, `rolename` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `sysuser` ( `id` int(4) NOT NULL, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `user_role` ( `id` int(4) NOT NULL, `user_id` int(4) DEFAULT NULL, `role_id` int(4) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
接下來(lái)是針對(duì)這三張表的實(shí)體類,Mapper接口以及xml文件,你可以不看代碼,主要實(shí)現(xiàn)一個(gè)通過(guò)用戶名查找用戶以及相關(guān)權(quán)限的操作:
@Data public class Roles { private Integer id; private String roleName; } @Data public class SysUser { private Integer id; private String userName; private String password; private List<Roles> roles; }
Mapper接口:
public interface UserMapper { public SysUser getUserByUserName(@Param("userName") String userName); }
xml實(shí)現(xiàn):
<?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.javayz.springsecurity.mapper.UserMapper"> <resultMap id="userMap" type="com.javayz.springsecurity.entity.SysUser"> <id property="id" column="ID"/> <result property="userName" column="username"/> <result property="password" column="password"/> <collection property="roles" ofType="com.javayz.springsecurity.entity.Roles"> <result column="name" property="roleName"/> </collection> </resultMap> <select id="getUserByUserName" parameterType="String" resultMap="userMap"> select sysuser.*,roles.rolename from sysuser LEFT JOIN user_role on sysuser.id= user_role.user_id LEFT JOIN roles on user_role.role_id=roles.id where username= #{userName} </select> </mapper>
3.2 認(rèn)證過(guò)程
SpringSecurity的認(rèn)證過(guò)程是這樣的,首先通過(guò)用戶名或者其他唯一的ID在數(shù)據(jù)庫(kù)里找到這個(gè)用戶,用戶的密碼以非對(duì)稱加密的方式存儲(chǔ)。取到用戶后將前臺(tái)傳入的密碼加密后和數(shù)據(jù)庫(kù)中已經(jīng)加密好的字段進(jìn)行對(duì)比,從而通過(guò)認(rèn)證。
上面這個(gè)過(guò)程中的第一步通過(guò)用戶名找到用戶的操作需要通過(guò)Service服務(wù)來(lái)實(shí)現(xiàn),并且這個(gè)Service服務(wù)需要繼承SpringSecurity中的UserDetailsService接口。這個(gè)接口返回一個(gè)SpringSecurity的User對(duì)象。
@Service public class UserService implements UserDetailsService { @Resource private UserMapper userMapper; //根據(jù)用戶名找到對(duì)應(yīng)的用戶信息 @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { SysUser sysUser = userMapper.getUserByUserName(s); if (sysUser!=null){ List<GrantedAuthority> roles=new ArrayList<>(); sysUser.getRoles().stream().forEach(x->{ roles.add(new SimpleGrantedAuthority(x.getRoleName())); }); return new User(sysUser.getUserName(),sysUser.getPassword(),roles); } throw new UsernameNotFoundException("用戶未找到"); } }
3.3 Security攔截配置
上面的步驟都完成后就開始配置Security了,寫一個(gè)配置方法SecurityConfig,代碼層面很簡(jiǎn)單,認(rèn)證傳入userService對(duì)象,會(huì)自動(dòng)把數(shù)據(jù)庫(kù)中取出的密碼和前端傳過(guò)來(lái)的密碼進(jìn)行對(duì)照。同時(shí)在userService中還傳入了roles集合,在授權(quán)處給不同的頁(yè)面附上不同的權(quán)限即可。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; //授權(quán) @Override protected void configure(HttpSecurity http) throws Exception { //首頁(yè)所有人都能訪問(wèn),level頁(yè)面只有有權(quán)限的人才能訪問(wèn) http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); //沒(méi)有權(quán)限默認(rèn)跳到登陸頁(yè),默認(rèn)會(huì)重定向到/login http.formLogin(); } //認(rèn)證 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder()); } }
3.4 其他注意點(diǎn)
我在認(rèn)證的時(shí)候使用的密碼加密方式是BCryptPasswordEncoder,因此存入數(shù)據(jù)庫(kù)中的密碼也需要被加密,常用的方式就是在注冊(cè)時(shí)通過(guò)同樣的方式對(duì)密碼進(jìn)行加密存入數(shù)據(jù)庫(kù)中:
String password="xxx"; BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); String encode=bCryptPasswordEncoder.encode(password);
(四)總結(jié)
SpringSecurity很強(qiáng)大,除了這種方式之外,還支持集成JWT、Oauth2等等。后續(xù)我會(huì)繼續(xù)更新,我是魚仔,我們下期再見(jiàn)。
到此這篇關(guān)于SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證的文章就介紹到這了,更多相關(guān)SpringBoot+SpringSecurity授權(quán)認(rè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用戶認(rèn)證和授權(quán)的實(shí)現(xiàn)
- SpringBoot整合SpringSecurity認(rèn)證與授權(quán)
- 深入淺析springsecurity入門登錄授權(quán)
- SpringSecurityOAuth2實(shí)現(xiàn)微信授權(quán)登錄
- springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐
- SpringSecurity數(shù)據(jù)庫(kù)進(jìn)行認(rèn)證和授權(quán)的使用
- SpringSecurity授權(quán)機(jī)制的實(shí)現(xiàn)(AccessDecisionManager與投票決策)
相關(guān)文章
關(guān)于kafka消費(fèi)不到遠(yuǎn)程bootstrap-server?數(shù)據(jù)的問(wèn)題
很多朋友遇到kafka消費(fèi)不到遠(yuǎn)程bootstrap-server?數(shù)據(jù)的問(wèn)題,怎么解決這個(gè)問(wèn)題,很多朋友不知所措,下面小編給大家?guī)?lái)了關(guān)于kafka消費(fèi)不到遠(yuǎn)程bootstrap-server?數(shù)據(jù)的問(wèn)題及解決方法,感興趣的朋友跟隨小編一起看看吧2021-11-11mybatis-plus阻止全表更新與刪除的實(shí)現(xiàn)
BlockAttackInnerInterceptor 是mybatis-plus的一個(gè)內(nèi)置攔截器,用于防止惡意的全表更新或刪除操作,本文主要介紹了mybatis-plus阻止全表更新與刪除的實(shí)現(xiàn),感興趣的可以了解一下2023-12-12SpringBoot接受參數(shù)相關(guān)注解方式
SpringBoot接受參數(shù)的注解包括@PathVariable、@RequestHeader、@RequestParameter、@CookieValue、@RequestBody、@RequestAttribute和@SessionAttribute等,每個(gè)注解都有詳細(xì)的使用方法和示例代碼2024-12-12ThreadLocal內(nèi)存泄漏常見(jiàn)要點(diǎn)解析
這篇文章主要介紹了ThreadLocal內(nèi)存泄漏常見(jiàn)要點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11tio-http-server打包為二進(jìn)制文件的實(shí)現(xiàn)及優(yōu)勢(shì)詳解
這篇文章主要為大家介紹了tio-http-server打包為二進(jìn)制文件實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12