SpringBoot整合Spring Security的詳細(xì)教程
好好學(xué)習(xí),天天向上
本文已收錄至我的Github倉(cāng)庫(kù)DayDayUP:github.com/RobodLee/DayDayUP,歡迎Star,更多文章請(qǐng)前往:目錄導(dǎo)航
前言
Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問(wèn)控制框架。提供了完善的認(rèn)證機(jī)制和方法級(jí)的授權(quán)功能。是一款非常優(yōu)秀的權(quán)限管理框架。它的核心是一組過(guò)濾器鏈,不同的功能經(jīng)由不同的過(guò)濾器。這篇文章就是想通過(guò)一個(gè)小案例將Spring Security整合到SpringBoot中去。要實(shí)現(xiàn)的功能就是在認(rèn)證服務(wù)器上登錄,然后獲取Token,再訪問(wèn)資源服務(wù)器中的資源。
基本概念
單點(diǎn)登錄
什么叫做單點(diǎn)登錄呢。就是在一個(gè)多應(yīng)用系統(tǒng)中,只要在其中一個(gè)系統(tǒng)上登錄之后,不需要在其它系統(tǒng)上登錄也可以訪問(wèn)其內(nèi)容。舉個(gè)例子,京東那么復(fù)雜的系統(tǒng)肯定不會(huì)是單體結(jié)構(gòu),必然是微服務(wù)架構(gòu),比如訂單功能是一個(gè)系統(tǒng),交易是一個(gè)系統(tǒng)......那么我在下訂單的時(shí)候登錄了,付錢(qián)難道還需要再登錄一次嗎,如果是這樣,用戶體驗(yàn)也太差了吧。實(shí)現(xiàn)的流程就是我在下單的時(shí)候系統(tǒng)發(fā)現(xiàn)我沒(méi)登錄就讓我登錄,登錄完了之后系統(tǒng)返回給我一個(gè)Token,就類似于身份證的東西;然后我想去付錢(qián)的時(shí)候就把Token再傳到交易系統(tǒng)中,然后交易系統(tǒng)驗(yàn)證一下Token就知道是誰(shuí)了,就不需要再讓我登錄一次。
JWT
上面提到的Token就是JWT(JSON Web Token),是一種用于通信雙方之間傳遞安全信息的簡(jiǎn)潔的、URL安全的表述性聲明規(guī)范。一個(gè)JWT實(shí)際上就是一個(gè)字符串,它由三部分組成,頭部、載荷與簽名。為了能夠直觀的看到JWT的結(jié)構(gòu),我畫(huà)了一張思維導(dǎo)圖:
最終生成的JWT令牌就是下面這樣,有三部分,用 .
分隔。
base64UrlEncode(JWT 頭)+"."+base64UrlEncode(載荷)+"."+HMACSHA256(base64UrlEncode(JWT 頭) + "." + base64UrlEncode(有效載荷),密鑰)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
RSA
從上面的例子中可以看出,JWT在加密解密的時(shí)候都用到了同一個(gè)密鑰 “ robod666 ”,這將會(huì)帶來(lái)一個(gè)弊端,如果被黑客知道了密鑰的內(nèi)容,那么他就可以去偽造Token了。所以為了安全,我們可以使用非對(duì)稱加密算法RSA。
RSA的基本原理有兩點(diǎn):
- 私鑰加密,持有私鑰或公鑰才可以解密
- 公鑰加密,持有私鑰才可解密
認(rèn)證服務(wù)器用戶登錄功能
前期準(zhǔn)備
介紹完了基本概念之后就可以開(kāi)始整合了,受限于篇幅,只貼最核心的代碼,其它內(nèi)容請(qǐng)小伙伴們?nèi)ピ创a中找,地址在文末。 首先需要準(zhǔn)備好數(shù)據(jù)庫(kù):
-- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號(hào)', `ROLE_NAME` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名稱', `ROLE_DESC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色描述', PRIMARY KEY (`ID`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES (1, 'ROLE_USER', '基本角色'); INSERT INTO `sys_role` VALUES (2, 'ROLE_ADMIN', '超級(jí)管理員'); INSERT INTO `sys_role` VALUES (3, 'ROLE_PRODUCT', '管理產(chǎn)品'); INSERT INTO `sys_role` VALUES (4, 'ROLE_ORDER', '管理訂單'); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用戶名稱', `password` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密碼', `status` int(1) NULL DEFAULT 1 COMMENT '1開(kāi)啟0關(guān)閉', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of sys_user -- ---------------------------- INSERT INTO `sys_user` VALUES (1, 'xiaoming', '$2a$10$CYX9OMv0yO8wR8rE19N2fOaXDJondci5uR68k2eQJm50q8ESsDMlC', 1); INSERT INTO `sys_user` VALUES (2, 'xiaoma', '$2a$10$CYX9OMv0yO8wR8rE19N2fOaXDJondci5uR68k2eQJm50q8ESsDMlC', 1); -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `UID` int(11) NOT NULL COMMENT '用戶編號(hào)', `RID` int(11) NOT NULL COMMENT '角色編號(hào)', PRIMARY KEY (`UID`, `RID`) USING BTREE, INDEX `FK_Reference_10`(`RID`) USING BTREE, CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `sys_role` (`ID`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES (1, 1); INSERT INTO `sys_user_role` VALUES (2, 1); INSERT INTO `sys_user_role` VALUES (1, 3); INSERT INTO `sys_user_role` VALUES (2, 4); SET FOREIGN_KEY_CHECKS = 1;
一共三張表,分別是用戶表,角色表,用戶-角色表。用戶是登錄用的,密碼其實(shí)就是加密過(guò)的字符串,內(nèi)容是“ 123 ”;角色是做權(quán)限控制時(shí)用的。
然后創(chuàng)建一個(gè)空的父工程SpringSecurityDemo,然后在父工程里面創(chuàng)建一個(gè)Module作為認(rèn)證服務(wù),名叫authentication_server。添加必要的依賴。(內(nèi)容較占篇幅,有需要的去源碼中獲取,源碼地址見(jiàn)文末
)。
項(xiàng)目的配置文件內(nèi)容截取了核心的部分貼在下面:
………… # 配置了公鑰和私鑰的位置 rsa: key: pubKeyPath: C:\Users\robod\Desktop\auth_key\id_key_rsa.pub priKeyPath: C:\Users\robod\Desktop\auth_key\id_key_rsa
最后的公私鑰的標(biāo)簽是自定義的,并不是Spring提供的標(biāo)簽,后面我們會(huì)在RSA的配置類中去加載這一部分內(nèi)容。
為了方便起見(jiàn),我們還可以準(zhǔn)備幾個(gè)工具類(內(nèi)容較占篇幅,有需要的去源碼中獲取,源碼地址見(jiàn)文末
):
- JsonUtils:提供了json相關(guān)的一些操作;
- JwtUtils:生成token以及校驗(yàn)token相關(guān)方法;
- RsaUtils:生成公鑰私鑰文件,以及從文件中讀取公鑰私鑰。
我們可以將載荷單獨(dú)封裝成一個(gè)對(duì)象:
@Data public class Payload<T> { private String id; private T userInfo; private Date expiration; }
現(xiàn)在再去寫(xiě)一個(gè)測(cè)試類,調(diào)用RsaUtils中的相應(yīng)方法去生成公鑰和私鑰。那公鑰私鑰生成好了在使用的時(shí)候是怎么獲取的呢?為了解決這個(gè)問(wèn)題,我們需要?jiǎng)?chuàng)建一個(gè)RSA的配置類,
@Data @ConfigurationProperties("rsa.key") //指定配置文件的key public class RsaKeyProperties { private String pubKeyPath; private String priKeyPath; private PublicKey publicKey; private PrivateKey privateKey; @PostConstruct public void createKey() throws Exception { this.publicKey = RsaUtils.getPublicKey(pubKeyPath); this.privateKey = RsaUtils.getPrivateKey(priKeyPath); } }
首先我們使用了@ConfigurationProperties注解去指定公鑰私鑰路徑的key,然后在構(gòu)造方法中就可以去獲取到公鑰私鑰的內(nèi)容了。這樣在需要公鑰私鑰的時(shí)候就可以直接調(diào)用這個(gè)類了。但是不放入Spring容器中怎么調(diào)用這個(gè)類,所以在啟動(dòng)類中添加一個(gè)注解:
@EnableConfigurationProperties(RsaKeyProperties.class)
這表示把RSA的配置類放入Spring容器中。
用戶登錄
在實(shí)現(xiàn)用戶登錄的功能之前,先說(shuō)一下登錄的相關(guān)內(nèi)容。關(guān)于登錄流程我在網(wǎng)上看了篇文章感覺(jué)挺好的,貼出來(lái)給小伙伴們看看:
vue+springboot前后端分離實(shí)現(xiàn)單點(diǎn)登錄跨域問(wèn)題解決方法
首先會(huì)進(jìn)入U(xiǎn)sernamePasswordAuthenticationFilter并且設(shè)置權(quán)限為null和是否授權(quán)為false,然后進(jìn)入ProviderManager查找支持UsernamepasswordAuthenticationToken的provider并且調(diào)用provider.authenticate(authentication);再然后就是UserDetailsService
接口的實(shí)現(xiàn)類(也就是自己真正具體的業(yè)務(wù)了),這時(shí)候都檢查過(guò)了后,就會(huì)回調(diào)UsernamePasswordAuthenticationFilter并且設(shè)置權(quán)限(具體業(yè)務(wù)所查出的權(quán)限)和設(shè)置授權(quán)為true(因?yàn)檫@時(shí)候確實(shí)所有關(guān)卡都檢查過(guò)了)。
在上面這段話中,提到了一個(gè)UsernamePasswordAuthenticationFilter,我們一開(kāi)始進(jìn)入的就是這個(gè)過(guò)濾器的attemptAuthentication()方法,但是這個(gè)方法是從form表單中獲取用戶名密碼,和我們的需求不符,所以我們需要重寫(xiě)這個(gè)方法。然后經(jīng)過(guò)一系列的周轉(zhuǎn),進(jìn)入到了UserDetailsService.loadUserByUsername()方法中,所以我們?yōu)榱藢?shí)現(xiàn)自己的業(yè)務(wù)邏輯,需要去實(shí)現(xiàn)這個(gè)方法。這個(gè)方法返回的是一個(gè)UserDetails接口對(duì)象,如果想返回自定義的對(duì)象,可以去實(shí)現(xiàn)這個(gè)接口。最終用戶驗(yàn)證成功之后,調(diào)用的是UsernamePasswordAuthenticationFilter的父類AbstractAuthenticationProcessingFilter.successfulAuthentication()方法,我們也需要去重寫(xiě)這個(gè)方法去實(shí)現(xiàn)我們自己的需求。
所以現(xiàn)在就來(lái)實(shí)現(xiàn)一下上面說(shuō)的這些東西吧👇
@Data public class SysUser implements UserDetails { private Integer id; private String username; private String password; private Integer status; private List<SysRole> roles = new ArrayList<>(); //SysRole封裝了角色信息,和登錄無(wú)關(guān),我放在后面講 //這里還有幾個(gè)UserDetails中的方法,我就不貼代碼了 }
我們自定義了一個(gè)SysUser類去實(shí)現(xiàn)UserDetails接口,然后添加了幾個(gè)自定義的字段☝
public interface UserService extends UserDetailsService { } //----------------------------------------------------------- @Service("userService") public class UserServiceImpl implements UserService { ………… @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser sysUser = userMapper.findByUsername(username); return sysUser; } }
在☝這段代碼中,我們先定義了一個(gè)接口UserService去繼承UserDetailsService,然后用UserServiceImpl實(shí)現(xiàn)了UserService,就相當(dāng)于UserServiceImpl實(shí)現(xiàn)了UserDetailsService,這樣我們就可以去實(shí)現(xiàn)loadUserByUsername()方法,內(nèi)容很簡(jiǎn)單,就是用用戶名去數(shù)據(jù)庫(kù)中查出對(duì)應(yīng)的SysUser,然后具體的驗(yàn)證流程就可以交給其它的過(guò)濾器去實(shí)現(xiàn)了,我們就不用管了。
前面提到了需要去重寫(xiě)attemptAuthentication()和successfulAuthentication()方法,那就自定義一個(gè)過(guò)濾器去繼承UsernamePasswordAuthenticationFilter然后重寫(xiě)這兩個(gè)方法吧👇
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; private RsaKeyProperties rsaKeyProperties; public JwtLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) { this.authenticationManager = authenticationManager; this.rsaKeyProperties = rsaKeyProperties; } //這個(gè)方法是用來(lái)去嘗試驗(yàn)證用戶的 @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { SysUser user = JSONObject.parseObject(request.getInputStream(),SysUser.class); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( user.getUsername(), user.getPassword()) ); } catch (Exception e) { try { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); PrintWriter out = response.getWriter(); Map<String, Object> map = new HashMap<>(); map.put("code", HttpServletResponse.SC_UNAUTHORIZED); map.put("message", "賬號(hào)或密碼錯(cuò)誤!"); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close(); } catch (Exception e1) { e1.printStackTrace(); } throw new RuntimeException(e); } } //成功之后執(zhí)行的方法 @Override public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SysUser sysUser = new SysUser(); sysUser.setUsername(authResult.getName()); sysUser.setRoles((List<SysRole>) authResult.getAuthorities()); String token = JwtUtils.generateTokenExpireInMinutes(sysUser,rsaKeyProperties.getPrivateKey(),24*60); response.addHeader("Authorization", "RobodToken " + token); //將Token信息返回給用戶 try { //登錄成功時(shí),返回json格式進(jìn)行提示 response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter out = response.getWriter(); Map<String, Object> map = new HashMap<String, Object>(4); map.put("code", HttpServletResponse.SC_OK); map.put("message", "登陸成功!"); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close(); } catch (Exception e1) { e1.printStackTrace(); } } }
代碼的邏輯還是很清晰的,我就不去講解了。
現(xiàn)在重點(diǎn)來(lái)了,Spring Security怎么知道我們要去調(diào)用自己的UserService和自定義的過(guò)濾器呢?所以我們需要配置一下,這也是使用Spring Security的一個(gè)核心——>配置類👇
@Configuration @EnableWebSecurity //這個(gè)注解的意思是這個(gè)類是Spring Security的配置類 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ………… @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //認(rèn)證用戶的來(lái)源 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } //配置SpringSecurity相關(guān)信息 @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() //關(guān)閉csrf .addFilter(new JwtLoginFilter(super.authenticationManager(),rsaKeyProperties)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); //禁用session } }
在配置類中,配置了認(rèn)證用戶的來(lái)源和添加了自定義的過(guò)濾器。這樣就可以實(shí)現(xiàn)登錄的功能了。
可以看到,現(xiàn)在已經(jīng)成功登錄了,但是這個(gè)/login是從哪兒來(lái)的呢,這個(gè)是Spring Security自己提供的,用戶名的鍵必須是”username“,密碼的鍵必須是 ”password“,提交方式必須是POST。
總結(jié)一下,實(shí)現(xiàn)登錄的功能需要做哪些操作:
- 認(rèn)證用戶實(shí)現(xiàn)UserDetails接口
- 用戶來(lái)源的Service實(shí)現(xiàn)UserDetailsService接口,實(shí)現(xiàn)loadUserByUsername()方法,從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)
- 實(shí)現(xiàn)自己的過(guò)濾器繼承UsernamePasswordAuthenticationFilter,重寫(xiě)attemptAuthentication()和successfulAuthentication()方法實(shí)現(xiàn)自己的邏輯
- Spring Security的配置類繼承自WebSecurityConfigurerAdapter,重寫(xiě)里面的兩個(gè)config()方法
- 如果使用RSA非對(duì)稱加密,就準(zhǔn)備好RSA的配置類,然后在啟動(dòng)類中加入注解將其加入IOC容器中
資源服務(wù)器權(quán)限校驗(yàn)
在這一小節(jié),我們要實(shí)現(xiàn)去訪問(wèn)資源服務(wù)器中的資源,并進(jìn)行鑒權(quán)的操作。在父工程SpringSecirityDemo中再創(chuàng)建一個(gè)模塊recourse_server。因?yàn)槲覀儸F(xiàn)在并不需要從數(shù)據(jù)庫(kù)中獲取用戶信息。所以就不需要自己去定義Service和Mapper了。也不需要登錄的過(guò)濾器了。下面這張目錄結(jié)構(gòu)圖是資源服務(wù)工程所需要的所有東西。
SysRole上一節(jié)中用到了但是沒(méi)有詳細(xì)說(shuō)明。這個(gè)類是用來(lái)封裝角色信息的,做鑒權(quán)的時(shí)候用的,實(shí)現(xiàn)了GrantedAuthority接口:
@Data public class SysRole implements GrantedAuthority { private Integer id; private String roleName; private String roleDesc; /** * 如果授予的權(quán)限可以當(dāng)作一個(gè)String的話,就可以返回一個(gè)String * @return */ @JsonIgnore @Override public String getAuthority() { return roleName; } }
里面實(shí)現(xiàn)了getAuthority方法,直接返回roleName即可。roleName是角色名。
客戶端將Token傳到資源服務(wù)器中,服務(wù)器需要對(duì)Token進(jìn)行校驗(yàn)并取出其中的載荷信息。所以我們可以自定義一個(gè)過(guò)濾器繼承自BasicAuthenticationFilter,然后重寫(xiě)doFilterInternal()方法,實(shí)現(xiàn)自己的邏輯。
public class JwtVerifyFilter extends BasicAuthenticationFilter { ………… @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader("Authorization"); //沒(méi)有登錄 if (header == null || !header.startsWith("RobodToken ")) { chain.doFilter(request, response); response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); PrintWriter out = response.getWriter(); Map<String, Object> map = new HashMap<String, Object>(4); map.put("code", HttpServletResponse.SC_FORBIDDEN); map.put("message", "請(qǐng)登錄!"); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close(); return; } //登錄之后從token中獲取用戶信息 String token = header.replace("RobodToken ",""); SysUser sysUser = JwtUtils.getInfoFromToken(token, rsaKeyProperties.getPublicKey(), SysUser.class).getUserInfo(); if (sysUser != null) { Authentication authResult = new UsernamePasswordAuthenticationToken (sysUser.getUsername(),null,sysUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); } } }
在這段代碼中,先是從請(qǐng)求頭中獲取"Authorization"的值,如果值未null或者不是以我們規(guī)定的 “RobodToken ” 開(kāi)頭就說(shuō)明不是我們?cè)O(shè)置的Token,就是沒(méi)登錄,提示用戶登錄。有Token的話就調(diào)用JwtUtils.getInfoFromToken()去驗(yàn)證并獲取載荷的內(nèi)容。驗(yàn)證通過(guò)的話就在Authentication的構(gòu)造方法中把角色信息傳進(jìn)去,然后交給其它過(guò)濾器去執(zhí)行即可。
私鑰應(yīng)該只保存在認(rèn)證服務(wù)器中,所以資源服務(wù)器中只要存公鑰就可以了。
………… rsa: key: pubKeyPath: C:\Users\robod\Desktop\auth_key\id_key_rsa.pub
@Data @ConfigurationProperties("rsa.key") //指定配置文件的key public class RsaKeyProperties { private String pubKeyPath; private PublicKey publicKey; @PostConstruct public void createKey() throws Exception { this.publicKey = RsaUtils.getPublicKey(pubKeyPath); } }
接下來(lái)就是Spring Security核心的配置文件了👇
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true) //開(kāi)啟權(quán)限控制的注解支持,securedEnabled表示SpringSecurity內(nèi)部的權(quán)限控制注解開(kāi)關(guān) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ………… //配置SpringSecurity相關(guān)信息 @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() //關(guān)閉csrf .authorizeRequests() .antMatchers("/**").hasAnyRole("USER") //角色信息 .anyRequest() //其它資源 .authenticated() //表示其它資源認(rèn)證通過(guò)后 .and() .addFilter(new JwtVerifyFilter(super.authenticationManager(),rsaKeyProperties)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); //禁用session } }
這里面有個(gè)注解 @EnableGlobalMethodSecurity(securedEnabled = true),這個(gè)注解的意思是開(kāi)啟權(quán)限控制的注解支持。然后添加了自定義的Token解析過(guò)濾器。最后在需要進(jìn)行權(quán)限控制的方法上添加注解即可👇
@RestController @RequestMapping("/product") public class ProductController { @Secured("ROLE_PRODUCT") @RequestMapping("/findAll") public String findAll() { return "產(chǎn)品列表查詢成功"; } }
好了,這樣findAll方法就需要有"ROLE_PRODUCT"權(quán)限才能訪問(wèn)。我們來(lái)測(cè)試一下:
登錄成功之后,響應(yīng)頭中有服務(wù)器返回的Token信息,把它復(fù)制下來(lái),然后添加到我們請(qǐng)求的請(qǐng)求頭中。
可以看到,現(xiàn)在已經(jīng)成功訪問(wèn)到資源了。再來(lái)?yè)Q個(gè)沒(méi)有權(quán)限的用戶登錄測(cè)試一下:
請(qǐng)求被拒絕了,說(shuō)明權(quán)限控制功能是沒(méi)有問(wèn)題的??偨Y(jié)一下步驟:
- 封裝權(quán)限信息的類實(shí)現(xiàn)GrantedAuthority接口,并實(shí)現(xiàn)里面的getAuthority()方法
- 實(shí)現(xiàn)自己的Token校驗(yàn)過(guò)濾器繼承自BasicAuthenticationFilter,并重寫(xiě)doFilterInternal()方法,實(shí)現(xiàn)自己的業(yè)務(wù)邏輯
- 編寫(xiě)Spring Security的配置類繼承WebSecurityConfigurerAdapter,重寫(xiě)configure()方法添加自定義的過(guò)濾器,并添加@EnableGlobalMethodSecurity(securedEnabled = true)注解開(kāi)啟注解權(quán)限控制的功能
- 如果使用RSA非對(duì)稱加密,就準(zhǔn)備好RSA的配置類,然后在啟動(dòng)類中加入注解將其加入IOC容器中,注意這里不要只要配置公鑰即可
總結(jié)
SpringBoot 整合 Spring Security到這里就結(jié)束了。文章只是簡(jiǎn)單的說(shuō)了一下整合的流程,很多其它的東西都沒(méi)有說(shuō),比如各個(gè)過(guò)濾器都有什么作用等。還有,這里采用的認(rèn)證服務(wù)器和資源服務(wù)器分離的方式,要是集成在一起也是可以的。類似的問(wèn)題還有很多,小伙伴們就自行研究吧。問(wèn)了讓文章不會(huì)太臃腫,很多代碼都沒(méi)有貼出來(lái),有需要的小伙伴點(diǎn)擊下面的鏈接就可以下載了。
到此這篇關(guān)于SpringBoot整合Spring Security的文章就介紹到這了,更多相關(guān)SpringBoot整合Spring Security內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ShardingSphere jdbc集成多數(shù)據(jù)源的實(shí)現(xiàn)步驟
本文主要介紹了ShardingSphere jdbc集成多數(shù)據(jù)源的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10SpringBoot+layuimini實(shí)現(xiàn)左側(cè)菜單動(dòng)態(tài)展示的示例代碼
Layuimini是Layui的升級(jí)版,它是專業(yè)做后臺(tái)頁(yè)面的框架,而且是適合PC端和移動(dòng)端,以下地址可以在PC端顯示,也可以在手機(jī)上顯示,只不過(guò)會(huì)做自適應(yīng),本文將給大家介紹了SpringBoot+layuimini實(shí)現(xiàn)左側(cè)菜單動(dòng)態(tài)展示的方法,需要的朋友可以參考下2024-04-04流讀取導(dǎo)致StringBuilder.toString()亂碼的問(wèn)題及解決
這篇文章主要介紹了流讀取導(dǎo)致StringBuilder.toString()亂碼的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11Java設(shè)計(jì)模式之模板方法模式Template Method Pattern詳解
在我們實(shí)際開(kāi)發(fā)中,如果一個(gè)方法極其復(fù)雜時(shí),如果我們將所有的邏輯寫(xiě)在一個(gè)方法中,那維護(hù)起來(lái)就很困難,要替換某些步驟時(shí)都要重新寫(xiě),這樣代碼的擴(kuò)展性就很差,當(dāng)遇到這種情況就要考慮今天的主角——模板方法模式2022-11-11Java平臺(tái)調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug
這篇文章主要介紹了Java平臺(tái)調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Spring?Cloud集成Nacos?Config動(dòng)態(tài)刷新源碼剖析
這篇文章主要為大家介紹了Spring?Cloud集成Nacos?Config動(dòng)態(tài)刷新源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08spring入門(mén)教程之bean的繼承與自動(dòng)裝配詳解
眾所周知Spring里面的bean就類似是定義的一個(gè)組件,而這個(gè)組件的作用就是實(shí)現(xiàn)某個(gè)功能的,下面這篇文章主要給大家介紹了關(guān)于spring入門(mén)教程之bean繼承與自動(dòng)裝配的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11