spring security+jwt 登录认证

1.综述

Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的
成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方
案( 用户认证与用户授权)。

2.版本与环境

idea2022
maven3.6+
jdk1.8
security 5.7.2

3.架构

在这里插入图片描述

4.数据库认证逻辑图

在这里插入图片描述

5.案例 security+jwt

5.1引入依赖

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--缺少此jar包,导致@Mapper注解无效-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

5.2新建工具类

JwtUtil

package com.gz.security.utils;


import com.gz.security.config.JwtConfig;
import com.gz.security.constant.Constant;
import com.gz.security.pojo.auth.AccessToken;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.date.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;

/**
 * @author: GZ
 * @description: JWT 工具类
 *
 * <p>
 * 一个完整的JwtToken由三部分组成:头部+负载信息+签名
 * header 存放JwtToken签名的算法 | token的类型:{"alg": "HS512","typ": "JWT"}
 * payload 主要存放用户名、创建时间、生成时间:{"sub":"RenHe","created":1489079981393,"exp":1489684781}
 * signature 生成算法:HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 * <p>
 */
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class JwtUtil {


    private final JwtConfig jwtConfig;

    /**
     * 从request中获取token
     *
     * @param request
     * @return
     */
    public String getToken(HttpServletRequest request) {

    	return request.getHeader(Constant.REQUEST_HEADER);
    }

    /**
     * 生成token
     *
     * @param subject
     * @return
     */
    public AccessToken generateToken(String subject) {
        final Date now = new Date();
        Long expired = Long.valueOf(jwtConfig.getExpiredTime());
        final Date expiredTime = new Date(now.getTime() + expired * 1000);
        String token = Constant.TOKEN_PREFIX + Jwts.builder()
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expiredTime)
                .signWith(SignatureAlgorithm.HS512, jwtConfig.getApiSecretKey())
                .compact();
        return AccessToken.builder().loginAccount(subject).falconToken(token).expiredTime(expiredTime).build();
    }

    /**
     * 验证token
     *
     * @param token    token from client
     * @param username user details from database
     */
    public boolean validateToken(String token, String username) {
        Claims claims = getClaimsFromToken(token);
        return claims.getSubject().equals(username) && !isTokenExpired(claims);
    }

    /**
     * 刷新token
     *
     * @param oldToken token with tokenHead
     */
    public AccessToken refreshToken(String oldToken) {
        String token = oldToken.substring(Constant.TOKEN_PREFIX.length());
        Claims claims = getClaimsFromToken(token);
        if (tokenRefreshJustBefore(claims)) {
            return AccessToken.builder().loginAccount(claims.getSubject()).falconToken(oldToken).expiredTime(claims.getExpiration()).build();
        } else {
            return generateToken(claims.getSubject());
        }
    }

    /**
     * 检查token在指定时间内是否被刷新
     */
    private boolean tokenRefreshJustBefore(Claims claims) {
        Date refreshDate = new Date();
        if (refreshDate.after(claims.getExpiration()) && refreshDate.before(DateUtil.offsetSecond(claims.getExpiration(), 1800))) {
            return true;
        }
        return false;
    }

    /**
     * 从token中获取Claims
     *
     * @param token
     * @return
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(jwtConfig.getApiSecretKey())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("JWT de-resolve fail, token expired or incorrect, token: {}", token);
        }
        return claims;
    }

    /**
     * 从token中获取用户信息
     */
    public String getSubjectFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        if (Objects.nonNull(claims)) {
            return claims.getSubject();
        } else {
            return null;
        }
    }

    /**
     * 检查是否过期
     */
    private boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }


}

AuthUtil

package com.gz.security.utils;

import com.gz.security.constant.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @author: GZ
 * @description: 授权工具类
 */
@Slf4j
public class AuthUtil {

    /**
     * Retrieve login account from token
     *
     * @param authToken
     * @param jwtProvider
     * @return
     */
    public static String getLoginAccountFromToken(String authToken, JwtUtil jwtProvider) {

        if (StringUtils.isEmpty(authToken) || !authToken.startsWith(Constant.TOKEN_PREFIX)) {
            log.info("token is empty or not start with Bearer");
            return StringUtils.EMPTY;
        }

        // 移除token前缀 (Bearer )
        authToken = authToken.substring(Constant.TOKEN_PREFIX.length());

        // 从token获取登录账户
        String loginAccount = jwtProvider.getSubjectFromToken(authToken);

        return loginAccount;
    }

}

5.2新建组件类

CustomAccessDeniedHandler

package com.gz.security.component;

import com.gz.security.pojo.auth.JsonAuthentication;
import com.gz.security.pojo.response.WebResponseBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author: GZ
 * @description: 禁止访问处理 403
 */
@Component
public class CustomAccessDeniedHandler extends JsonAuthentication implements AccessDeniedHandler {

	private static final Log logger = LogFactory.getLog(CustomAccessDeniedHandler.class);

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
			throws IOException {
		logger.info("Pre-authenticated entry point called. Access denied");
		response.setStatus(HttpServletResponse.SC_FORBIDDEN);
		this.writeJSON(response, WebResponseBuilder.fail("403","禁止访问"));
	}
}

CustomAuthenticationEntryPoint

package com.gz.security.component;


import com.gz.security.pojo.auth.JsonAuthentication;
import com.gz.security.pojo.response.WebResponseBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author: GZ
 * @description: 未授权处理 401 用来解决匿名用户访问无权限资源时的异常
 */
@Component
public class CustomAuthenticationEntryPoint extends JsonAuthentication implements AuthenticationEntryPoint {

	private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
			throws IOException {
		logger.info("Authenticated entry point called. Unauthorized");
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		this.writeJSON(response, WebResponseBuilder.fail("401","未授权"));
	}
}

CustomAuthenticationFailureHandler


package com.gz.security.component;


import com.gz.security.pojo.auth.JsonAuthentication;
import com.gz.security.pojo.response.WebResponseBuilder;
import lombok.extern.slf4j.Slf4j;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * @author GuoZhong
 * @description 认证失败服务出口
 * @date 2022/10/11 10:03
 */

@Slf4j
@Component
public class CustomAuthenticationFailureHandler  extends JsonAuthentication implements AuthenticationFailureHandler {
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
		response.setStatus(HttpServletResponse.SC_OK);
		// 处理异常
		if (exception instanceof InternalAuthenticationServiceException) {
			this.writeJSON(
					response,
					WebResponseBuilder.fail(
							"500",exception.getMessage()));
		} else if (exception instanceof BadCredentialsException) {

			this.writeJSON(
					response,
					WebResponseBuilder.fail(
							"500",exception.getMessage()));
		} else {
			// 输出错误信息
			this.writeJSON(
					response,
					WebResponseBuilder.fail(
							"500",exception.getMessage()));
		}

	}
}


CustomAuthenticationProvider

package com.gz.security.component;


import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;



/**
 * @author: GZ
 * @date: 2021/9/6 11:11
 * @description: 登录身份验证
 */
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
	    User user=null;
        try {
             user  = (User) userDetailsService.loadUserByUsername(username);
        } catch (UsernameNotFoundException e) {
            throw new InternalAuthenticationServiceException("用户不存在");
        }

	    return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

CustomAuthenticationSuccessHandler


package com.gz.security.component;

import com.gz.security.config.RedisConfig;
import com.gz.security.constant.RedisKey;

import com.gz.security.pojo.auth.JsonAuthentication;
import com.gz.security.pojo.response.WebResponseBuilder;
import com.gz.security.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;


/**
 * @author GuoZhong
 * @description 登录成功出口
 * @date 2022/10/11 10:07
 */

@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomAuthenticationSuccessHandler extends JsonAuthentication implements AuthenticationSuccessHandler {

	private final JwtUtil jwtUtil;

	private final RedisTemplate redisTemplate;

	private final RedisConfig redisConfig;


	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
			throws IOException {
		response.setStatus(HttpServletResponse.SC_OK);
		User loginAccount = (User) authentication.getPrincipal();
		String username = loginAccount.getUsername();
        //存储用户信息,设置过期时间
		redisTemplate.opsForValue().set(
				RedisKey.getLoginAccountKey(username),
				loginAccount,
				Long.valueOf(redisConfig.getExpiredTime()),
				TimeUnit.MINUTES);

		// 输出正确响应信息
		this.writeJSON(response, WebResponseBuilder.success(jwtUtil.generateToken(username)));
	}


}

CustomLogoutSuccessHandler

package com.gz.security.component;

import com.gz.security.constant.RedisKey;
import com.gz.security.pojo.auth.JsonAuthentication;
import com.gz.security.pojo.response.WebResponseBuilder;
import com.gz.security.utils.AuthUtil;
import com.gz.security.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author: RenHe
 * @date: 2021/9/6 11:11
 * @description: 登出成功处理
 */
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomLogoutSuccessHandler extends JsonAuthentication implements LogoutSuccessHandler {

    private final JwtUtil jwtUtil;

    private final RedisTemplate redisTemplate;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException {

        String authToken = jwtUtil.getToken(request);
        String loginAccount = AuthUtil.getLoginAccountFromToken(authToken, jwtUtil);
        redisTemplate.delete(RedisKey.getLoginAccountKey(loginAccount));
        response.setStatus(HttpServletResponse.SC_OK);
        this.writeJSON(response, WebResponseBuilder.success("退出成功"));
    }
}

5.3新建配置类

SecurityConfig

package com.gz.security.config;

import com.gz.security.component.*;
import com.gz.security.filter.JwtAuthenticationTokenFilter;
import lombok.RequiredArgsConstructor;
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.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * @author GZ
 * security核心配置类
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
	/**
	 * 默认授权白名单
	 */
	private static final String DEFAULT_AUTH_WHITE_LIST = "/login";

	/**
	 * 用户名
	 */
	private static final String USERNAME_PARAMETER = "username";

	/**
	 * 密码
	 */
	private static final String PASSWORD_PARAMETER = "password";

	/**
	 * 登录
	 */
	private static final String LOGIN = "/login";

	/**
	 * 登出
	 */
	private static final String LOGOUT = "/logout";


	private final CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
	private final CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
	private final CustomAuthenticationEntryPoint authenticationEntryPoint;
	private final CustomAccessDeniedHandler accessDeniedHandler;
	private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
	private final LogoutSuccessHandler customLogoutSuccessHandler;


	/**
	 * 安全认证过滤链配置
	 */
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.formLogin()
				.loginProcessingUrl(LOGIN)
				.usernameParameter(USERNAME_PARAMETER)
				.passwordParameter(PASSWORD_PARAMETER)
				//成功出口
				.successHandler(customAuthenticationSuccessHandler)
				//失败出口
				.failureHandler(customAuthenticationFailureHandler)
				.and()
				.logout()
				.logoutUrl(LOGOUT)
				.clearAuthentication(true)
				//退出出口
				.logoutSuccessHandler(customLogoutSuccessHandler)
				.and()
				.authorizeRequests()
				// 放行所有OPTIONS请求
				.antMatchers(HttpMethod.OPTIONS).permitAll()
				// 放行白名单地址
				.antMatchers(DEFAULT_AUTH_WHITE_LIST).permitAll()
				.and()
				// 打开Spring Security的跨域,需要新增配置文件
				.cors()
				.and()
				// 关闭CSRF
				.csrf().disable()
				// 添加未登录与权限不足异常处理器
				.exceptionHandling()
				//未授权
				.authenticationEntryPoint(authenticationEntryPoint)
				//禁止访问
				.accessDeniedHandler(accessDeniedHandler)
				// 将自定义的JWT过滤器放到过滤链中
				.and()
				.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
				// 关闭Session机制
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
		return http.build();
	}


	/**
	 * 认证管理器
	 */
	@Bean
	public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
		return authenticationConfiguration.getAuthenticationManager();
	}


	/**
	 * 加密方式
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}

JwtConfig

package com.gz.security.config;

import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author: GZ
 * @description: JWT配置
 */
@Configuration
@ConfigurationProperties("jwt")
@Data
@ToString
public class JwtConfig {

    /**
     * 密钥
     */
    private String apiSecretKey = "JWT_SECRET_KEY";

    /**
     * 过期时间 默认7天
     */
    private String expiredTime = "10080";

}

CorsConfig


package com.gz.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.ArrayList;
import java.util.List;


/**
 * @author GuoZhong
 * @description 跨域请求配置
 */

@Configuration
public class CorsConfig {
	/**
	 * 可跨域请求
	 *
	 * @return
	 */
	@Bean
	public CorsFilter corsFilter() {
		final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
		final CorsConfiguration corsConfiguration = new CorsConfiguration();
		corsConfiguration.setAllowCredentials(true);
		List<String> allowedOriginPatterns = new ArrayList<>();
		allowedOriginPatterns.add("*");
		corsConfiguration.addAllowedOrigin("*");
		corsConfiguration.addAllowedHeader("*");
		corsConfiguration.addAllowedMethod("*");
		urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
		return new CorsFilter(urlBasedCorsConfigurationSource);
	}

}

AuthenticationConfig

package com.gz.security.config;

import com.gz.security.component.CustomAuthenticationProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * @author GuoZhong
 * @description Authentication 全局配置
 * @date 2022/10/16 12:35
 */
@Configuration
@RequiredArgsConstructor
public class AuthenticationConfig extends GlobalAuthenticationConfigurerAdapter {
	private final CustomAuthenticationProvider customAuthenticationProvider;
	private final UserDetailsService userDetailsService;

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		auth.eraseCredentials(false)
				.authenticationProvider(customAuthenticationProvider)
				.userDetailsService(userDetailsService);
	}
}

5.4新建过滤类

JwtAuthenticationTokenFilter

package com.gz.security.filter;

import com.gz.security.constant.Constant;
import com.gz.security.constant.RedisKey;
import com.gz.security.pojo.auth.UserInfo;
import com.gz.security.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author GZ
 *  JWT 登录过滤器
 */
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

	private final JwtUtil jwtUtil;

	private final RedisTemplate redisTemplate;


	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain chain)
			throws ServletException, IOException {
		log.info("JWT filter verification token from request header to  auto login start");

		// 从请求头获取授权信息
		String authToken = jwtUtil.getToken(request);

		if (StringUtils.isEmpty(authToken) || !authToken.startsWith(Constant.TOKEN_PREFIX)) {
			log.info("Token is empty or not start with Bearer");
			chain.doFilter(request, response);
			return;
		}

		// 移除token前缀'Bearer '
		authToken = authToken.substring(Constant.TOKEN_PREFIX.length());

		// 从token中获取登录用户
		String username = jwtUtil.getSubjectFromToken(authToken);

		if (StringUtils.isEmpty(username)) {
			log.info("Login account is empty");
			chain.doFilter(request, response);
			return;
		}

		// 从缓存中获取用户信息
		UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(RedisKey.getLoginAccountKey(username));

		// 验证token是否过期
		if (Objects.isNull(userInfo)) {
			log.info("Token is invalid");
			chain.doFilter(request, response);
			return;
		}

		// TODO 校验网关 后期放到网关去校验
        if (!jwtUtil.validateToken(authToken, userInfo.getUsername())) {
            log.info("Token is invalid");
            chain.doFilter(request, response);
            return;
        }


		// 创建authentication
		UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
				userInfo, userInfo.getPassword(),null);

		// 设置authentication到context
		SecurityContextHolder.getContext().setAuthentication(authentication);

		chain.doFilter(request, response);

		log.info("JWT filter verification token from request header to  auto login successfully end, user : {}", userInfo);
	}

}

6.登录访问,接口访问

在这里插入图片描述
在这里插入图片描述

7.项目地址

https://gitee.com/GZ-jelly/gz-login-spring-boot-starter.git

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫果冻

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值