spring security + jwt

47 阅读2分钟

SpringSecurityConfig:

package com.hhk.springBoot.config.security;


import java.util.List;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.hhk.springBoot.config.security.component.JWTAuthenticationFilter;
import com.hhk.springBoot.pojo.User;
import com.hhk.springBoot.service.UserService;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {
	@Autowired
	private JWTAuthenticationFilter jwtAuthenticationFilter;
	@Autowired
	private UserService userService;
	@Autowired
	private JsonAuthenticationEntryPoint jsonAuthenticationEntryPoint;
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
		http
		.csrf(csrf->csrf.disable())
		.authorizeHttpRequests(authz-> authz
				.requestMatchers("/public/**","/swagger-ui/**","/v3/api-docs/**").permitAll()
				.anyRequest().authenticated()
//				.anyRequest().permitAll()
			)
		.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class)
		.sessionManagement((session) -> session
	            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
	     )
		.httpBasic(Customizer.withDefaults());
//	添加自定义未登录结果返回
		http.exceptionHandling((excep)->excep
				.authenticationEntryPoint(jsonAuthenticationEntryPoint));
		return http.build();
	}
	
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
	
	@Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
//        authenticationProvider.setUserDetailsService(userDetailsService);
//        authenticationProvider.setPasswordEncoder(passwordEncoder);
        ProviderManager providerManager = new ProviderManager(authenticationProvider);
        // 可以在这里设置更多的ProviderManager属性
        return providerManager;
    }
}

JsonAuthenticationEntryPoint:

package com.hhk.springBoot.config.security;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class JsonAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, 
                         HttpServletResponse response, 
                         AuthenticationException authException) throws IOException {
        // 设置响应状态码和内容类型
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        // 构建自定义 JSON 响应体
        Map<String, Object> body = new HashMap<>();
        body.put("code", 401);
        body.put("message", "未登录,请先认证--");
        body.put("path", request.getRequestURI());
        
        // 将 Map 转换为 JSON 并写入响应流
        new ObjectMapper().writeValue(response.getOutputStream(), body);
    }
}

JWTAuthenticationFilter:

package com.hhk.springBoot.config.security.component;

import java.io.IOException;

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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.hhk.springBoot.service.impl.CustomUserDeatailsService;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter{

@Autowired
private CustomUserDeatailsService userDetailsService;

	@Override
	protected void doFilterInternal(HttpServletRequest request, 
																																	HttpServletResponse response, 
																																	FilterChain filterChain) throws ServletException, IOException {
																																		System.out.println("JWTAuthenticationFilter doFilterInternal-------------------------");
					String header = request.getHeader("Authorization");
					System.out.println("header:"+header);
					JwtUtil jwtUtil = new JwtUtil();
					if (header != null && header.startsWith("Bearer ")) {
									String token = header.substring(7);
									System.out.println("token:"+token);
									if (jwtUtil.validateToken(token) && userDetailsService!=null) {
													String username = jwtUtil.parseToken(token);
													System.out.println("userDetailsService"+userDetailsService);
													// 加载用户权限
													UserDetails userDetails = userDetailsService.loadUserByUsername(username);
													System.out.println("token:"+username);
													// System.out.println("token:"+userDetails);
													// 设置安全上下文
													UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
																	userDetails, null, userDetails.getAuthorities());
													SecurityContextHolder.getContext().setAuthentication(auth);
									}
					}
					filterChain.doFilter(request, response);
	}
	
}

JwtUtil:

package com.hhk.springBoot.config.security.component;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtUtil{
	private static final String CLAIM_KEY_USERNAME="sub";
	private static final String CLAIM_KEY_CREATE="created";
	private static final String secret="woyouyigemengxiangnajiushidailingjiarentuopingwoyouyigemengxiangnajiushidailingjiarentuoping";
	private static final Long expiration = (long) 604800;
	    // 生成 JWT
		public String generateToken(UserDetails userDetails) {
				Map<String,Object> claims=new HashMap<>();
				claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
				claims.put(CLAIM_KEY_CREATE,new Date());
				System.out.println("claims:"+claims);
				return generateToken(claims);
		}
		//    从token中获取登录用户名
		public String getUserNameFromToken(String token){
			String userName;
			try{
							Claims claims=getClaimFromToken(token);
							userName=claims.getSubject();
			}catch (Exception e){
							userName=null;
			}
			return userName;
	}
	//    从token中获取荷载
	private Claims getClaimFromToken(String token){
		Claims claims = null;
		try{
			claims= Jwts.parserBuilder()
			.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
			.build()
			.parseClaimsJws(token)
			.getBody();
			return claims;
		}catch (Exception e){
						e.printStackTrace();
		}
		return claims;
	}
	// 解析 JWT
	public String parseToken(String token) {
		return Jwts.parserBuilder()
										.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
										.build()
										.parseClaimsJws(token)
										.getBody()
										.getSubject();
	}
	//验证token是否失效
	public boolean validateToken(String token,UserDetails userDetails){
					String userName = getUserNameFromToken(token);
					return userName.equals(userDetails.getUsername())&&!isTokenExpired(token);
	}

	//判断token是否失效
	private boolean isTokenExpired(String token){
					Date expireDate=getExpiredDateFromToken(token);
					return expireDate.before(new Date());
	}
	//    判断token是否可以被刷新
	public boolean canRefresh(String token){
		return !isTokenExpired(token);
	}
	//    根据荷载生成jwt Token
	private String generateToken(Map<String,Object> claims){
					try{
									return Jwts.builder()
																	.setClaims(claims)
																	.setExpiration(generateExpirationDate())
																	.signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)),SignatureAlgorithm.HS512)
																	.compact();
					}catch (ExpiredJwtException e){
									e.getClaims();
									return null;
					}
	}
	//刷新token
	public String refreshToken(String token){
		Claims claims = getClaimFromToken(token);
		claims.put(CLAIM_KEY_CREATE,new Date());
		return generateToken(claims);
		}
		//    从token中获取过期时间
		private Date getExpiredDateFromToken(String token){
			Claims claims = getClaimFromToken(token);
			return claims.getExpiration();
		}
	//验证 JWT 有效性
	public boolean validateToken(String token) {
					try {
						String parseToken = parseToken(token);
						System.out.println("parseToken:"+parseToken+"-----isTokenExpired:"+isTokenExpired(token));
								if (parseToken == null) {
											return false;
								}else{
									return true;
								}
								// return isTokenExpired(token);
					} catch (JwtException | IllegalArgumentException e) {
									return false;
					}
	}
	//生成token失效时间
	private Date generateExpirationDate(){
		return new Date(System.currentTimeMillis()+expiration*1000);
	}
	
}