一看就懂!基於Springboot 攔截器的前後端分離式登錄攔截

之前寫過一篇關於Springboot+Shiro實現前後端分離的權限管理系統。但是由於使用了框架,對於一些小系統,實在用不上Shiro,而且還要加上學習成本。今天就來用原生的Spring技術實現登錄攔截。

前後端分離
要實現前後端分離,需要考慮以下2個問題: 1. 項目不再基於session了,如何知道訪問者是誰? 2. 如何確認訪問者的權限?
前後端分離,一般都是通過token實現,本項目也是一樣;用戶登錄時,生成token及 token過期時間,token與用戶是一一對應關係,調用接口的時候,把token放到header或 請求參數中,服務端就知道是誰在調用接口。
源碼先雙手奉上:https://github.com/FENGZHIJIE1998/Auth-demo

各位客官覺得好用記得給個Star!

項目架構圖

導包、配置文件都省略。

第一步編寫自己的攔截器AuthInteceptor 


public class AuthInterceptor implements HandlerInterceptor {
    @Autowired
    private AuthService authService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String token = TokenUtil.getRequestToken(request);
        //如果token爲空
        if (StringUtils.isBlank(token)) {
            setReturn(response,400,"用戶未登錄,請先登錄");
            return false;
        }
        //1. 根據token,查詢用戶信息
        UserEntity userEntity = authService.findByToken(token);
        //2. 若用戶不存在,
        if (userEntity == null) {
            setReturn(response,400,"用戶不存在");
            return false;
        }
        //3. token失效
        if (userEntity.getExpireTime().isBefore(LocalDateTime.now())) {
            setReturn(response,400,"用戶登錄憑證已失效,請重新登錄");
            return false;
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }
    //返回錯誤信息
    private static void setReturn(HttpServletResponse response, int status, String msg) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
        //UTF-8編碼
        httpResponse.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        Result build = Result.build(status, msg);
        String json = JSON.toJSONString(build);
        httpResponse.getWriter().print(json);
    }

}

第二步,將攔截器配置進Spring

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {


    @Bean
    public AuthInterceptor authInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 放行路徑
        List<String> patterns = new ArrayList();
        patterns.add("/webjars/**");
        patterns.add("/druid/**");
        patterns.add("/sys/login");
        patterns.add("/swagger/**");
        patterns.add("/v2/api-docs");
        patterns.add("/swagger-ui.html");
        patterns.add("/swagger-resources/**");
        patterns.add("/login");
        registry.addInterceptor(authInterceptor()).addPathPatterns("/**")
                                                .excludePathPatterns(patterns);
    }
}

第三步:測試

Controller 層

/**
 * 登錄校驗
 */
@RestController("/")
public class AuthController {

    @Autowired
    private AuthService authService;

    /**
     * 登錄
     *
     * @param loginDTO
     * @return token登錄憑證
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO) {
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        //用戶信息
        UserEntity user = authService.findByUsername(username);
        //賬號不存在、密碼錯誤
        if (user == null || !user.getPassword().equals(password)) {
            return Result.build(400, "用戶名或密碼錯誤");
        } else {
            //生成token,並保存到數據庫
            String token = authService.createToken(user);
            TokenVO tokenVO = new TokenVO();
            tokenVO.setToken(token);
            return Result.ok(tokenVO);
        }
    }

    /**
     * 登出
     *
     * @param
     * @return
     */
    @PostMapping("/logout")
    public Result logout(HttpServletRequest request) {
        //從request中取出token
        String token = TokenUtil.getRequestToken(request);
        authService.logout(token);
        return Result.ok();
    }

    /**
     * 測試
     *
     * @param
     * @return
     */
    @PostMapping("/test")
    public Result test( ) {

        return Result.ok("恭喜你,驗證成功啦,我可以返回數據給你");
    }

}


編寫Service層

@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private UserRepository userRepository;


    @Override
    public UserEntity findByUsername(String username) {
        return userRepository.findByUsername(username);

    }

    //12小時後失效
    private final static int EXPIRE = 12;
    
    @Override
    public String createToken(UserEntity user) {
        //用UUID生成token
        String token = UUID.randomUUID().toString();
        //當前時間
        LocalDateTime now = LocalDateTime.now();
        //過期時間
        LocalDateTime expireTime = now.plusHours(EXPIRE);
        //保存到數據庫
        user.setLoginTime(now);
        user.setExpireTime(expireTime);
        user.setToken(token);
        userRepository.save(user);
        return token;
    }

    @Override
    public void logout(String token) {
        UserEntity userEntity = userRepository.findByToken(token);
        //用UUID生成token
        token = UUID.randomUUID().toString();
        userEntity.setToken(token);
        userRepository.save(userEntity);

    }

    @Override
    public UserEntity findByToken(String token) {
        return userRepository.findByToken(token);
    }
}

 

然後啓動項目即可

首先數據庫保存一個用戶,用戶管理這裏就不做啦。

我們來看看效果:使用Swagger查看效果

未登錄狀態下:

傳遞錯誤的token:

登錄成功:

傳遞正確的token:

token過期之後

 

總結:如果你看過我那篇關於Shiro前後端分離的架構,其實很容易發現,這不就是簡化版的Shiro嗎?攔截、驗證、返回錯誤信息。這就是思想上的一致。Shiro底層是用Filter(過濾器)、HandlerInterceptor底層是Interceptor(攔截器)。此外,本篇不僅僅可以實現登錄攔截,對於權限攔截也是妥妥的,只需要再寫一個攔截器,對權限進行驗證,這裏就不展開了。最主要還是前後端分離的思想,由前端控制路由,後端只負責返回對應信息,這肯定是發展趨勢。

 


有什麼問題可以評論或者私信我,每日在線解(LIAO)疑(SAO)。

我是大誌,一位準備996的卑微碼農🐶,覺得好用記得點贊收藏!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章