赞
踩
<!--spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- java-jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
其余依赖包按需加入。
由于spring boot3.0废弃了extends WebSecurityConfigurerAdapter 的方式,所以这里采用添加@Bean新方式
package com.mia.common.config; import com.mia.common.filter.JwtRequestFilter; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.List; @Slf4j @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private AuthenticationConfiguration authenticationConfiguration; @Bean public JwtRequestFilter authenticationJwtTokenFilter() { return new JwtRequestFilter(); } /** * 加密方式 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 认证管理器,登录的时候参数会传给 authenticationManager * */ @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { //关闭csrf http.csrf().disable() // 允许跨域(也可以不允许,看具体需求) .cors().and() //不通过Session获取SecurityContext .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // 配置路径是否需要认证 .authorizeRequests() // 对于登录接口 允许匿名访问 .antMatchers(HttpMethod.POST,"/account/**") .permitAll() // 配置权限 .antMatchers("/test") .hasAuthority("admin") // 除上面外的所有请求全部需要鉴权认证 .anyRequest() .authenticated() .and() .authenticationManager(authenticationManager(authenticationConfiguration)) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //此处为添加jwt过滤 .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class) ; http.headers().frameOptions().disable(); return http.build(); } /** *跨域资源配置 */ @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); //此处发现如果不加入自己的项目地址,会被拦截。 configuration.setAllowedOriginPatterns(List.of("http://localhost:8083")); configuration.setAllowedMethods(List.of("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH")); configuration.setAllowedHeaders(List.of("Access-Control-Allow-Origin", "X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization")); configuration.setAllowCredentials(true); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
此处为以上过滤链中前置过滤中配置的过滤器,并且也是jwt与security主要集成使用的过滤类
package com.mia.common.filter; import com.mia.common.utils.JwtTokenUtil; import com.mia.loginProjet.account.UserDetailsServiceImpl; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; public class JwtRequestFilter extends OncePerRequestFilter { private static final Logger log = LoggerFactory.getLogger(JwtRequestFilter.class); public static final String BEARER = "Bearer "; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private UserDetailsServiceImpl userDetailsService; /** * 从 Authorization 标头中,提取令牌 * */ private String parseJwt(HttpServletRequest request) { String headerAuth = request.getHeader("Authorization"); if (StringUtils.hasText(headerAuth) && headerAuth.startsWith(BEARER)) { return headerAuth.substring(BEARER.length()); } return null; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwt = parseJwt(request); if (jwt != null && jwtTokenUtil.validateJwtToken(jwt)) { String username = jwtTokenUtil.getUsernameFromJwtToken(jwt); // 如果令牌存在(存在逻辑自己编写),则加载令牌 UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } catch (Exception e) { log.error("无法设置用户认证:{}", e); } filterChain.doFilter(request, response); } }
此处为jwt操作封装了一些工具类,为生成token和解析token数据所用
package com.mia.common.utils; import io.jsonwebtoken.*; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; @Configuration @Component @Slf4j public class JwtTokenUtil { /** * token的头key */ public static final String TOKEN_HEADER = "Authorization"; /** * token前缀 */ public static final String TOKEN_PREFIX = "Bearer "; /** * token 过期时间 30分钟 */ public static final long EXPIRATION = 1000 * 60 * 30; /** * 加密的key */ public static final String APP_SECRET_KEY = "secret"; /** * 权限的声明key */ private static final String ROLE_CLAIMS = "role"; /** * 生成token * * @param username 用户名 * @param role 用户角色 * @return token */ public static String createToken(String username, String role) { Map<String, Object> map = new HashMap<>(); map.put(ROLE_CLAIMS, role); String token = Jwts .builder() .setSubject(username) //.setClaims(map) // .claim("username", username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) .signWith(SignatureAlgorithm.HS256, APP_SECRET_KEY) .compact(); return TOKEN_PREFIX +token; } /** * 获取当前登录用户用户名 * * @param token * @return */ public static String getUsername(String token) { Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody(); return claims.get("username").toString(); } /** * 获取当前登录用户角色 * * @param token * @return */ public static String getUserRole(String token) { Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody(); return claims.get("rol").toString(); } /** * 检查token是否过期 * * @param token token * @return boolean */ public static boolean isExpiration(String token) { Claims claims = Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody(); return claims.getExpiration().before(new Date()); } public boolean validateJwtToken(String authToken) { try { Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(authToken); return true; } catch (SignatureException e) { log.error("Invalid JWT signature: {}", e.getMessage()); } catch (MalformedJwtException e) { log.error("Invalid JWT token: {}", e.getMessage()); } catch (ExpiredJwtException e) { log.error("JWT token is expired: {}", e.getMessage()); } catch (UnsupportedJwtException e) { log.error("JWT token is unsupported: {}", e.getMessage()); } catch (IllegalArgumentException e) { log.error("JWT claims string is empty: {}", e.getMessage()); } return false; } public String getUsernameFromJwtToken(String token) { return Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody().getSubject(); } }
这里需要自己去实现Security提供的UserDetailsService接口
重写loadUserByUsername方法,这里是验证token中信息是否可以允许访问的逻辑
package com.mia.loginProjet.account; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { GrantedAuthority grantedAuthority= () -> "roladmin"; List<GrantedAuthority> grantedAuthorities=new ArrayList<>(); grantedAuthorities.add(grantedAuthority); //这里可以实现从数据库取,我为了偷懒,就没这么做 - - return new User("admin","123",grantedAuthorities); } }
这里是我的例子,我偷懒写了一个登录接口,为了得到token,也为了以上过滤测试,可以实现成直接存在web头里,但我这里直接返回给前端处理,哈哈哈
package com.mia.loginProjet.account.controller; import com.mia.common.dto.LoginPwdDto; import com.mia.common.utils.JwtTokenUtil; import org.springframework.web.bind.annotation.*; @RestController public class accountController { @PostMapping("/account/login") public String login(@RequestBody LoginPwdDto loginPwdDto){ // 如果认证没通过,提示 // 认证通过返回jwt //TODO 数据库验证用户账号逻辑 我懒没实现 String jwt = JwtTokenUtil.createToken("admin","admin"); System.out.println("JWT--------"+jwt); //放入请求头 return jwt; } @GetMapping("/test") public String test1(){ return "200"; } @GetMapping("/test2") public String test2(){ return "200"; } }
至此结束示例,创作不易,可以收藏,但请不要抄袭
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。