SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
一、介紹
Spring Security是一個強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架。它是保護(hù)基于Spring的應(yīng)用程序的實(shí)際標(biāo)準(zhǔn)。Spring Security是一個可以為Java應(yīng)用程序提供全面安全服務(wù)的框架。同時,它也可以輕松擴(kuò)展以滿足自定義需求。
二、主要功能
Authentication (認(rèn)證),就是用戶登錄
Authorization (授權(quán)):一旦身份驗(yàn)證成功,判斷用戶擁有什么權(quán)限,可以訪問什么資源
防止跨站請求偽造(CSRF):Spring Security提供了內(nèi)置的防護(hù)機(jī)制,可以防止跨站請求偽造攻擊。
密碼存儲:Spring Security提供了多種密碼存儲格式,包括明文、加密和哈希。
集成其他安全框架:Spring Security可以與其他安全框架如OAuth2、JWT等進(jìn)行集成,以提供更全面的安全解決方案。
三、原理
? SpringSecurity的原理其實(shí)就是一個過濾器鏈,內(nèi)部包含了提供各種功能的過濾器。
1. SpringSecurity 過濾器鏈
SpringSecurity 采用的是責(zé)任鏈的設(shè)計模式,它有一條很長的過濾器鏈。
- SecurityContextPersistenceFilter:每次請求處理之前將該請求相關(guān)的安全上下文信息加載到 SecurityContextHolder 中。
- LogoutFilter:用于處理退出登錄。
- UsernamePasswordAuthenticationFilter:用于處理基于表單的登錄請求,從表單中獲取用戶名和密碼。
- BasicAuthenticationFilter:檢測和處理 http basic 認(rèn)證。
- ExceptionTranslationFilter:處理過濾器鏈中拋出的任何AccessDeniedException和AuthenticationException 。
- FilterSecurityInterceptor:負(fù)責(zé)權(quán)限校驗(yàn)的過濾器,可以看做過濾器鏈的出口。
- …
流程說明:客戶端發(fā)起一個請求,進(jìn)入 Security 過濾器鏈。
1.當(dāng)?shù)?LogoutFilter 的時候判斷是否是登出路徑,如果是登出路徑則到 logoutHandler ,如果登出成功則到logoutSuccessHandler 登出成功處理,如果登出失敗則由 ExceptionTranslationFilter ;如果不是登出路徑則直接進(jìn)入下一個過濾器。
2.當(dāng)?shù)?UsernamePasswordAuthenticationFilter 的時候判斷是否為登錄路徑,如果是,則進(jìn)入該過濾器進(jìn)行登錄操作,如果登錄失敗則到 AuthenticationFailureHandler 登錄失敗處理器處理,如果登錄成功則到 AuthenticationSuccessHandler 登錄成功處理器處理,如果不是登錄請求則不進(jìn)入該過濾器。
3.當(dāng)?shù)?FilterSecurityInterceptor 的時候會拿到 uri ,根據(jù) uri 去找對應(yīng)的鑒權(quán)管理器,鑒權(quán)管理器做鑒權(quán)工作,鑒權(quán)成功則到 Controller 層否則到 AccessDeniedHandler 鑒權(quán)失敗處理器處理。
2. JWT校驗(yàn)登錄的校驗(yàn)流程
首先前端一樣是把登錄信息發(fā)送給后端,后端查詢數(shù)據(jù)庫校驗(yàn)用戶的賬號和密碼是否正確,正確的話則使用jwt生成token,并且返回給前端。以后前端每次請求時,都需要攜帶token,后端獲取token后,使用jwt進(jìn)行驗(yàn)證用戶的token是否無效或過期,驗(yàn)證成功后才去做相應(yīng)的邏輯。
四、Spring Boot整合Redis、SpringSecurity、JWT的示例demo
- 添加依賴項在
pom.xml
文件中添加以下依賴項:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> </dependencies>`
- 創(chuàng)建Redis配置類
@Configuration public class RedisConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Bean public JedisConnectionFactory jedisConnectionFactory() { return new JedisConnectionFactory(new RedisStandaloneConfiguration(host, port)); } @Bean public RedisTemplate<String, Object> redisTemplate() { final RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory()); return template; } }
- 創(chuàng)建
JwtTokenUtil
類,用于生成和驗(yàn)證JWT令牌。
@Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -2550185165626007488L; private static final String secret = "mySecret"; public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } private String doGenerateToken(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + 5 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }`
- 創(chuàng)建
JwtAuthenticationEntryPoint
類,用于處理未經(jīng)授權(quán)的請求。
@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -7858869558953243875L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } }`
- 創(chuàng)建
JwtRequestFilter
類,用于解析和驗(yàn)證JWT令牌。
@Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String requestTokenHeader = request.getHeader("Authorization"); String username = null; String jwtToken = null; if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { jwtToken = requestTokenHeader.substring(7); try { username = jwtTokenUtil.getUsernameFromToken(jwtToken); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT Token"); } catch (ExpiredJwtException e) { System.out.println("JWT Token has expired"); } } else { logger.warn("JWT Token does not begin with Bearer String"); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken .setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } chain.doFilter(request, response); } }`
- 配置Spring Security
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private UserDetailsService jwtUserDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf().disable() .authorizeRequests().antMatchers("/authenticate").permitAll(). anyRequest().authenticated().and(). exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } }`
以上是簡單的Spring Boot整合Redis、Security、JWT和Redis的示例,可以根據(jù)自己的實(shí)際需求進(jìn)行調(diào)整。更多相關(guān)SpringBoot 統(tǒng)一鑒權(quán)認(rèn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程
- SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動態(tài)權(quán)限問題
- SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)
- SpringSecurity動態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
- springboot+jwt+springSecurity微信小程序授權(quá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)
- 深入淺析springsecurity入門登錄授權(quán)
- mall整合SpringSecurity及JWT實(shí)現(xiàn)認(rèn)證授權(quán)實(shí)戰(zhàn)
- SpringSecurity頁面授權(quán)與登錄驗(yàn)證實(shí)現(xiàn)(內(nèi)存取值與數(shù)據(jù)庫取值)
- SpringSecurity 鑒權(quán)與授權(quán)的具體使用
相關(guān)文章
java.io.EOFException產(chǎn)生原因及解決方法(附代碼)
java.io.EOFException表示在讀取數(shù)據(jù)時突然遇到了文件或流的末尾,也就是說客戶端或服務(wù)器已經(jīng)關(guān)閉了連接,但是你還在嘗試讀取數(shù)據(jù),這篇文章主要給大家介紹了關(guān)于java.io.EOFException產(chǎn)生原因及解決的相關(guān)資料,需要的朋友可以參考下2023-09-09SpringBoot處理JSON數(shù)據(jù)方法詳解
這篇文章主要介紹了SpringBoot整合Web開發(fā)中Json數(shù)據(jù)處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-10-10mybatis如何根據(jù)表逆向自動化生成代碼實(shí)例
逆向工程是一個專門為 MyBatis 框架使用者設(shè)計的代碼生成器,可以根據(jù)數(shù)據(jù)庫中的表字段名,自動生成 POJO 類,mapper 接口與 SQL 映射文件,這篇文章主要給大家介紹了關(guān)于mybatis如何根據(jù)表逆向自動化生成代碼的相關(guān)資料,需要的朋友可以參考下2021-08-08Springboot教程之如何設(shè)置springboot熱重啟
這篇文章主要介紹了Springboot教程之如何設(shè)置springboot熱重啟,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07使用Mybatis-plus實(shí)現(xiàn)對數(shù)據(jù)庫表的內(nèi)部字段進(jìn)行比較
這篇文章主要介紹了使用Mybatis-plus實(shí)現(xiàn)對數(shù)據(jù)庫表的內(nèi)部字段進(jìn)行比較方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07