最近写的项目中又写了一遍前后分离的微信授权登录功能(本人后端),顺便梳理下,不想看可直接看后面的干货
- 通过登录拦截强制未登录用户进入授权界面,无法直接访问其他页面
- 用户进入授权界面完成授权会返回code,后端根据code进行一系列操作后将用户注册保存至数据库(后端会进行一些处理比如判断主动授权、静默授权,是否更新过信息)
- 同时会生成token,后端将token保存在redis中,前端保存在storage中
- 前端在访问除登录请求或业务需要的请求外,前端都需要将token携带在请求头中,后端接收到token进行查找用户是否存在,校验token等操作
一、引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
二、Jwt工具类
@Slf4j
public class JwtUtil {
public static void main(String[] args) {
System.out.println(generate(1, DateUtil.date().offset(DateField.DAY_OF_MONTH, 1)));
System.out.println(getId("eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKZXJzZXktU2VjdXJpdHktQmFzaWMiLCJzdWIiOiIxIiwiZXhwIjoxNjI2MDA4NzcyLCJpYXQiOjE2MjU5MjIzNzJ9.arWl-hagLZ8xlfacXwUs-iSTjH3UVGIK68lEYMOYNPI"));
}
private static final String KEY = "1234qwer";
public static String generate(Integer id, Date expiration) {
if (id == null) {
throw new NullPointerException("null id is illegal");
}
if (expiration == null) {
throw new NullPointerException("null expiration is illegal");
}
return Jwts.builder()
.setIssuer("Jersey-Security-Basic")
.setSubject(id.toString())
.setExpiration(expiration)
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, KEY)
.compact();
}
public static boolean verify(String token) {
try {
Jwts.parser().setSigningKey(KEY).parseClaimsJws(token.trim());
return true;
} catch (Exception e) {
log.error("无效的token:{}", token);
return false;
}
}
public static Integer getId(String token) {
if (verify(token)) {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(KEY).parseClaimsJws(token);
return Convert.toInt(claimsJws.getBody().getSubject(), null);
}
return null;
}
}
三、新建WebMvc配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${savePath:/data/springboot-demo/file/}")
private String savePath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/media/**")
.addResourceLocations("file:" + savePath)
.setCacheControl(CacheControl.maxAge(864000, TimeUnit.SECONDS).cachePublic());
}
/**
* 拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor())//添加拦截器
.excludePathPatterns("/user/loginByCode", "/user/loginByOpenId")//对应的不拦截的请求
.addPathPatterns("/**"); //拦截所有请求
}
/**
* 自己写的拦截器
* @return
*/
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
}
四、新建登陆拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
UserMapper userMapper;
private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class);
/**
* 进入controller层之前拦截请求
*
* @param httpServletRequest
* @param
* @param o
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse response, Object o) throws Exception {
String token = httpServletRequest.getHeader("Authorization");
if (token == null) {
throw new RuntimeException("未携带token");
}
Integer id = JwtUtil.getId(token);
User user = userMapper.selectOne(new QueryWrapper<User>().eq("id", id));
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证token
boolean verify = JwtUtil.verify(token);
System.out.println(verify);
if (verify) {
return true;
}
return false;
}
//访问controller之后 访问视图之前被调用
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
log.info("--------------处理请求完成后视图渲染之前的处理操作---------------");
}
//访问视图之后被调用
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
log.info("---------------视图渲染之后的操作-------------------------0");
}