欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot使用JWT作为Token实现前后端分离登录

程序员文章站 2022-03-15 11:42:59
...

JWT就不多介绍了

传统的shiro和SpringSecurity安全框架需要Session,重启后Session会丢失,或者将Session存入redis也行,但是相对比较繁琐。利用JWT可以很轻松的实现Token的认证,在前后端分离的情况下,JWT也显得非常的简易。

依赖

<dependency>
	<groupId>com.auth0</groupId>
	<artifactId>java-jwt</artifactId>
	<version>3.4.0</version>
</dependency>

 

登录请求生成token,第一个参数传入ID,表明是哪个用户,第二个参数是对token进行加密,我们都用密码进行加密。

String token = JWT.create().withAudience(user.getId() + "")
				.sign(Algorithm.HMAC256(user.getPwd()));

 

写一个notoken注解,此注解适用于方法上,用来表明某个请求不需要token

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoToken {
	boolean required() default true;
}

 

然后利用拦截器进行拦截所有的请求

public class AuthenticationInterceptor implements HandlerInterceptor {
	@Autowired
	private UserMapper userMapper;

	@Override
	public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			Object object) throws Exception {

		// 如果不是映射到方法直接通过
		if (!(object instanceof HandlerMethod)) {
			return true;
		}
		HandlerMethod handlerMethod = (HandlerMethod) object;
		Method method = handlerMethod.getMethod();
		// 检查是否有passtoken注释,有则跳过认证
		if (method.isAnnotationPresent(NoToken.class)) {
			NoToken passToken = method.getAnnotation(NoToken.class);
			if (passToken.required()) {
				return true;
			}
		} else {
			String token = httpServletRequest.getHeader("Authorization");
			// Token不存在
			if (token == null) {
				throw new NeedLoginException();
			}
			// 获取用户ID,如果出错抛异常
			Integer userId;
			try {
				userId = AuthUtils.getUserId(httpServletRequest);
			} catch (JWTDecodeException j) {
				throw new NeedLoginException();
			}
			Staff user = userMapper.selectByPrimaryKey(userId);
			if (user == null) {
				throw new NeedLoginException();
			}
			// 验证Token是否有效
			JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getStaffPwd() + "")).build();
			try {
				jwtVerifier.verify(token);
			} catch (JWTVerificationException e) {
				throw new NeedLoginException();
			}
			return true;
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
			ModelAndView modelAndView) throws Exception {
	}

	@Override
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			Object o, Exception e) throws Exception {
	}
}

 

将拦截器加入到Bean中

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解
	}

	@Bean
	public AuthenticationInterceptor authenticationInterceptor() {
		return new AuthenticationInterceptor();
	}
}

 

从请求头获取Token解析用户ID

public class AuthUtils {
	public static Integer getUserId(HttpServletRequest req) {
		String token = req.getHeader("Authorization");
		String userId = JWT.decode(token).getAudience().get(0);
		return Integer.parseInt(userId);
	}
}

 

这样就可以实现登录了。

如果有哪些请求不需要Token的话,直接可以加上注解@NoToken,在拦截器中判断如果遇到此注解,则不会进行判断Token