飞道的博客

一看就懂!基于Springboot 拦截器的前后端分离式登录拦截

453人阅读  评论(0)

之前写过一篇关于Springboot+Shiro实现前后端分离的权限管理系统。但是由于使用了框架,对于一些小系统,实在用不上Shiro,而且还要加上学习成本。今天就来用原生的Spring技术实现登录拦截。

前后端分离
要实现前后端分离,需要考虑以下2个问题: 1. 项目不再基于session了,如何知道访问者是谁? 2. 如何确认访问者的权限?
前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。
源码先双手奉上:https://github.com/FENGZHIJIE1998/Auth-demo

各位客官觉得好用记得给个Star!

项目架构图

导包、配置文件都省略。

第一步编写自己的拦截器AuthInteceptor 


  
  1. public class AuthInterceptor implements HandlerInterceptor {
  2. @Autowired
  3. private AuthService authService;
  4. @Override
  5. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
  6. String token = TokenUtil.getRequestToken(request);
  7. //如果token为空
  8. if (StringUtils.isBlank(token)) {
  9. setReturn(response, 400, "用户未登录,请先登录");
  10. return false;
  11. }
  12. //1. 根据token,查询用户信息
  13. UserEntity userEntity = authService.findByToken(token);
  14. //2. 若用户不存在,
  15. if (userEntity == null) {
  16. setReturn(response, 400, "用户不存在");
  17. return false;
  18. }
  19. //3. token失效
  20. if (userEntity.getExpireTime().isBefore(LocalDateTime.now())) {
  21. setReturn(response, 400, "用户登录凭证已失效,请重新登录");
  22. return false;
  23. }
  24. return true;
  25. }
  26. @Override
  27. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
  28. }
  29. @Override
  30. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
  31. }
  32. //返回错误信息
  33. private static void setReturn(HttpServletResponse response, int status, String msg) throws IOException {
  34. HttpServletResponse httpResponse = (HttpServletResponse) response;
  35. httpResponse.setHeader( "Access-Control-Allow-Credentials", "true");
  36. httpResponse.setHeader( "Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
  37. //UTF-8编码
  38. httpResponse.setCharacterEncoding( "UTF-8");
  39. response.setContentType( "application/json;charset=utf-8");
  40. Result build = Result.build(status, msg);
  41. String json = JSON.toJSONString(build);
  42. httpResponse.getWriter().print(json);
  43. }
  44. }

第二步,将拦截器配置进Spring


  
  1. @Configuration
  2. public class InterceptorConfig implements WebMvcConfigurer {
  3. @Bean
  4. public AuthInterceptor authInterceptor() {
  5. return new AuthInterceptor();
  6. }
  7. @Override
  8. public void addInterceptors(InterceptorRegistry registry) {
  9. // 放行路径
  10. List<String> patterns = new ArrayList();
  11. patterns.add( "/webjars/**");
  12. patterns.add( "/druid/**");
  13. patterns.add( "/sys/login");
  14. patterns.add( "/swagger/**");
  15. patterns.add( "/v2/api-docs");
  16. patterns.add( "/swagger-ui.html");
  17. patterns.add( "/swagger-resources/**");
  18. patterns.add( "/login");
  19. registry.addInterceptor(authInterceptor()).addPathPatterns( "/**")
  20. .excludePathPatterns(patterns);
  21. }
  22. }

第三步:测试

Controller 层


  
  1. /**
  2. * 登录校验
  3. */
  4. @RestController( "/")
  5. public class AuthController {
  6. @Autowired
  7. private AuthService authService;
  8. /**
  9. * 登录
  10. *
  11. * @param loginDTO
  12. * @return token登录凭证
  13. */
  14. @PostMapping( "/login")
  15. public Result login(@RequestBody LoginDTO loginDTO) {
  16. String username = loginDTO.getUsername();
  17. String password = loginDTO.getPassword();
  18. //用户信息
  19. UserEntity user = authService.findByUsername(username);
  20. //账号不存在、密码错误
  21. if (user == null || !user.getPassword().equals(password)) {
  22. return Result.build( 400, "用户名或密码错误");
  23. } else {
  24. //生成token,并保存到数据库
  25. String token = authService.createToken(user);
  26. TokenVO tokenVO = new TokenVO();
  27. tokenVO.setToken(token);
  28. return Result.ok(tokenVO);
  29. }
  30. }
  31. /**
  32. * 登出
  33. *
  34. * @param
  35. * @return
  36. */
  37. @PostMapping( "/logout")
  38. public Result logout(HttpServletRequest request) {
  39. //从request中取出token
  40. String token = TokenUtil.getRequestToken(request);
  41. authService.logout(token);
  42. return Result.ok();
  43. }
  44. /**
  45. * 测试
  46. *
  47. * @param
  48. * @return
  49. */
  50. @PostMapping( "/test")
  51. public Result test( ) {
  52. return Result.ok( "恭喜你,验证成功啦,我可以返回数据给你");
  53. }
  54. }

编写Service层


  
  1. @Service
  2. public class AuthServiceImpl implements AuthService {
  3. @Autowired
  4. private UserRepository userRepository;
  5. @Override
  6. public UserEntity findByUsername(String username) {
  7. return userRepository.findByUsername(username);
  8. }
  9. //12小时后失效
  10. private final static int EXPIRE = 12;
  11. @Override
  12. public String createToken(UserEntity user) {
  13. //用UUID生成token
  14. String token = UUID.randomUUID().toString();
  15. //当前时间
  16. LocalDateTime now = LocalDateTime.now();
  17. //过期时间
  18. LocalDateTime expireTime = now.plusHours(EXPIRE);
  19. //保存到数据库
  20. user.setLoginTime(now);
  21. user.setExpireTime(expireTime);
  22. user.setToken(token);
  23. userRepository.save(user);
  24. return token;
  25. }
  26. @Override
  27. public void logout(String token) {
  28. UserEntity userEntity = userRepository.findByToken(token);
  29. //用UUID生成token
  30. token = UUID.randomUUID().toString();
  31. userEntity.setToken(token);
  32. userRepository.save(userEntity);
  33. }
  34. @Override
  35. public UserEntity findByToken(String token) {
  36. return userRepository.findByToken(token);
  37. }
  38. }

 

然后启动项目即可

首先数据库保存一个用户,用户管理这里就不做啦。

我们来看看效果:使用Swagger查看效果

未登录状态下:

传递错误的token:

登录成功:

传递正确的token:

token过期之后

 

总结:如果你看过我那篇关于Shiro前后端分离的架构,其实很容易发现,这不就是简化版的Shiro吗?拦截、验证、返回错误信息。这就是思想上的一致。Shiro底层是用Filter(过滤器)、HandlerInterceptor底层是Interceptor(拦截器)。此外,本篇不仅仅可以实现登录拦截,对于权限拦截也是妥妥的,只需要再写一个拦截器,对权限进行验证,这里就不展开了。最主要还是前后端分离的思想,由前端控制路由,后端只负责返回对应信息,这肯定是发展趋势。

 


有什么问题可以评论或者私信我,每日在线解(LIAO)疑(SAO)。

我是大誌,一位准备996的卑微码农🐶,觉得好用记得点赞收藏!!!

 


转载:https://blog.csdn.net/weixin_42236404/article/details/105252892
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场