SpringBoot整合SpringSecurity和JWT和Redis實現(xiàn)統(tǒng)一鑒權認證
一、介紹
Spring Security是一個強大且高度可定制的身份驗證和訪問控制框架。它是保護基于Spring的應用程序的實際標準。Spring Security是一個可以為Java應用程序提供全面安全服務的框架。同時,它也可以輕松擴展以滿足自定義需求。
二、主要功能
Authentication (認證),就是用戶登錄
Authorization (授權):一旦身份驗證成功,判斷用戶擁有什么權限,可以訪問什么資源
防止跨站請求偽造(CSRF):Spring Security提供了內(nèi)置的防護機制,可以防止跨站請求偽造攻擊。
密碼存儲:Spring Security提供了多種密碼存儲格式,包括明文、加密和哈希。
集成其他安全框架:Spring Security可以與其他安全框架如OAuth2、JWT等進行集成,以提供更全面的安全解決方案。
三、原理
? SpringSecurity的原理其實就是一個過濾器鏈,內(nèi)部包含了提供各種功能的過濾器。
1. SpringSecurity 過濾器鏈
SpringSecurity 采用的是責任鏈的設計模式,它有一條很長的過濾器鏈。
- SecurityContextPersistenceFilter:每次請求處理之前將該請求相關的安全上下文信息加載到 SecurityContextHolder 中。
- LogoutFilter:用于處理退出登錄。
- UsernamePasswordAuthenticationFilter:用于處理基于表單的登錄請求,從表單中獲取用戶名和密碼。
- BasicAuthenticationFilter:檢測和處理 http basic 認證。
- ExceptionTranslationFilter:處理過濾器鏈中拋出的任何AccessDeniedException和AuthenticationException 。
- FilterSecurityInterceptor:負責權限校驗的過濾器,可以看做過濾器鏈的出口。
- …
流程說明:客戶端發(fā)起一個請求,進入 Security 過濾器鏈。
1.當?shù)?LogoutFilter 的時候判斷是否是登出路徑,如果是登出路徑則到 logoutHandler ,如果登出成功則到logoutSuccessHandler 登出成功處理,如果登出失敗則由 ExceptionTranslationFilter ;如果不是登出路徑則直接進入下一個過濾器。
2.當?shù)?UsernamePasswordAuthenticationFilter 的時候判斷是否為登錄路徑,如果是,則進入該過濾器進行登錄操作,如果登錄失敗則到 AuthenticationFailureHandler 登錄失敗處理器處理,如果登錄成功則到 AuthenticationSuccessHandler 登錄成功處理器處理,如果不是登錄請求則不進入該過濾器。
3.當?shù)?FilterSecurityInterceptor 的時候會拿到 uri ,根據(jù) uri 去找對應的鑒權管理器,鑒權管理器做鑒權工作,鑒權成功則到 Controller 層否則到 AccessDeniedHandler 鑒權失敗處理器處理。
2. JWT校驗登錄的校驗流程
首先前端一樣是把登錄信息發(fā)送給后端,后端查詢數(shù)據(jù)庫校驗用戶的賬號和密碼是否正確,正確的話則使用jwt生成token,并且返回給前端。以后前端每次請求時,都需要攜帶token,后端獲取token后,使用jwt進行驗證用戶的token是否無效或過期,驗證成功后才去做相應的邏輯。
四、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
類,用于生成和驗證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)授權的請求。
@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
類,用于解析和驗證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ù)自己的實際需求進行調(diào)整。更多相關SpringBoot 統(tǒng)一鑒權認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Springboot整合SpringSecurity實現(xiàn)登錄認證和鑒權全過程
- SpringBoot整合SpringSecurityOauth2實現(xiàn)鑒權動態(tài)權限問題
- SpringBoot集成SpringSecurity和JWT做登陸鑒權的實現(xiàn)
- SpringSecurity動態(tài)加載用戶角色權限實現(xiàn)登錄及鑒權功能
- springboot+jwt+springSecurity微信小程序授權登錄問題
- SpringSecurity實現(xiàn)權限認證與授權的使用示例
- SpringSecurity進行認證與授權的示例代碼
- springSecurity用戶認證和授權的實現(xiàn)
- 深入淺析springsecurity入門登錄授權
- mall整合SpringSecurity及JWT實現(xiàn)認證授權實戰(zhàn)
- SpringSecurity頁面授權與登錄驗證實現(xiàn)(內(nèi)存取值與數(shù)據(jù)庫取值)
- SpringSecurity 鑒權與授權的具體使用
相關文章
java.io.EOFException產(chǎn)生原因及解決方法(附代碼)
java.io.EOFException表示在讀取數(shù)據(jù)時突然遇到了文件或流的末尾,也就是說客戶端或服務器已經(jīng)關閉了連接,但是你還在嘗試讀取數(shù)據(jù),這篇文章主要給大家介紹了關于java.io.EOFException產(chǎn)生原因及解決的相關資料,需要的朋友可以參考下2023-09-09SpringBoot處理JSON數(shù)據(jù)方法詳解
這篇文章主要介紹了SpringBoot整合Web開發(fā)中Json數(shù)據(jù)處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-10-10Springboot教程之如何設置springboot熱重啟
這篇文章主要介紹了Springboot教程之如何設置springboot熱重啟,本文通過實例圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07使用Mybatis-plus實現(xiàn)對數(shù)據(jù)庫表的內(nèi)部字段進行比較
這篇文章主要介紹了使用Mybatis-plus實現(xiàn)對數(shù)據(jù)庫表的內(nèi)部字段進行比較方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07