pom包导入
<!--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>
其余依赖包按需加入。
SecurityConfig配置
由于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过滤
此处为以上过滤链中前置过滤中配置的过滤器,并且也是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);
}
}
JwtTokenUtil
此处为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();
}
}
UserDetailsServiceImpl
这里需要自己去实现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";
}
}
至此结束示例,创作不易,可以收藏,但请不要抄袭