📑前言
本文主要讲了Spring Security文章,如果有什么需要改进的地方还请大佬指出⛺️
🎬作者简介:大家好,我是青衿🥇
☁️博客首页:CSDN主页放风讲故事
🌄每日一句:努力一点,优秀一点
目录
文章目录
Spring Security
一、介绍
Spring Security是一个强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的实际标准。Spring Security是一个可以为Java应用程序提供全面安全服务的框架。同时,它也可以轻松扩展以满足自定义需求。
二、主要功能
Authentication (认证),就是用户登录
Authorization (授权):一旦身份验证成功,判断用户拥有什么权限,可以访问什么资源
防止跨站请求伪造(CSRF):Spring Security提供了内置的防护机制,可以防止跨站请求伪造攻击。
密码存储:Spring Security提供了多种密码存储格式,包括明文、加密和哈希。
集成其他安全框架:Spring Security可以与其他安全框架如OAuth2、JWT等进行集成,以提供更全面的安全解决方案。
三、原理
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。
1. SpringSecurity 过滤器链
SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链。
- SecurityContextPersistenceFilter:每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中。
- LogoutFilter:用于处理退出登录。
- UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。
- BasicAuthenticationFilter:检测和处理 http basic 认证。
- ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException 。
- FilterSecurityInterceptor:负责权限校验的过滤器,可以看做过滤器链的出口。
- …
流程说明:客户端发起一个请求,进入 Security 过滤器链。
1.当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。
2.当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。
3.当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。
2. JWT校验登录的校验流程
首先前端一样是把登录信息发送给后端,后端查询数据库校验用户的账号和密码是否正确,正确的话则使用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>`
- 创建Redis配置类
@ConfigurationpublicclassRedisConfig{@Value("${spring.redis.host}")privateString host;@Value("${spring.redis.port}")privateint port;@BeanpublicJedisConnectionFactoryjedisConnectionFactory(){returnnewJedisConnectionFactory(newRedisStandaloneConfiguration(host, port));}@BeanpublicRedisTemplate<String,Object>redisTemplate(){finalRedisTemplate<String,Object> template =newRedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());return template;}}
- 创建
JwtTokenUtil
类,用于生成和验证JWT令牌。
@ComponentpublicclassJwtTokenUtilimplementsSerializable{privatestaticfinallong serialVersionUID =-2550185165626007488L;privatestaticfinalString secret ="mySecret";publicStringgetUsernameFromToken(String token){returngetClaimFromToken(token,Claims::getSubject);}publicDategetExpirationDateFromToken(String token){returngetClaimFromToken(token,Claims::getExpiration);}public<T>TgetClaimFromToken(String token,Function<Claims,T> claimsResolver){finalClaims claims =getAllClaimsFromToken(token);return claimsResolver.apply(claims);}privateClaimsgetAllClaimsFromToken(String token){returnJwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}privateBooleanisTokenExpired(String token){finalDate expiration =getExpirationDateFromToken(token);return expiration.before(newDate());}publicStringgenerateToken(UserDetails userDetails){Map<String,Object> claims =newHashMap<>();returndoGenerateToken(claims, userDetails.getUsername());}privateStringdoGenerateToken(Map<String,Object> claims,String subject){returnJwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(newDate(System.currentTimeMillis())).setExpiration(newDate(System.currentTimeMillis()+5*60*60*1000)).signWith(SignatureAlgorithm.HS512, secret).compact();}publicBooleanvalidateToken(String token,UserDetails userDetails){finalString username =getUsernameFromToken(token);return(username.equals(userDetails.getUsername())&&!isTokenExpired(token));}}`
- 创建
JwtAuthenticationEntryPoint
类,用于处理未经授权的请求。
@ComponentpublicclassJwtAuthenticationEntryPointimplementsAuthenticationEntryPoint,Serializable{privatestaticfinallong serialVersionUID =-7858869558953243875L;@Overridepublicvoidcommence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException)throwsIOException{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");}}`
- 创建
JwtRequestFilter
类,用于解析和验证JWT令牌。
@ComponentpublicclassJwtRequestFilterextendsOncePerRequestFilter{@AutowiredprivateMyUserDetailsService myUserDetailsService;@AutowiredprivateJwtTokenUtil jwtTokenUtil;@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain)throwsServletException,IOException{finalString 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 =newUsernamePasswordAuthenticationToken(
userDetails,null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(newWebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}
chain.doFilter(request, response);}}`
- 配置Spring Security
@Configuration@EnableWebSecuritypublicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{@AutowiredprivateJwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;@AutowiredprivateUserDetailsService jwtUserDetailsService;@AutowiredprivateJwtRequestFilter jwtRequestFilter;@AutowiredpublicvoidconfigureGlobal(AuthenticationManagerBuilder auth)throwsException{
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}@Bean@OverridepublicAuthenticationManagerauthenticationManagerBean()throwsException{returnsuper.authenticationManagerBean();}@Overrideprotectedvoidconfigure(HttpSecurity httpSecurity)throwsException{
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的示例,可以根据自己的实际需求进行调整。
📑文章末尾
版权归原作者 放风讲故事 所有, 如有侵权,请联系我们删除。