【實戰】Springboot +jjwt+注解實現需登錄才能操作


springboot +jjwt+注解實現需登錄才能調用接口

1.開發需要登錄才能進行操作的自定義注解NeedLogin,后面可以寫在需要登陸后操作的接口上

package com.songzhen.howcool.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

2.開發攔截器(Interceptor),預處理全部請求

package com.songzhen.howcool.auth;

import com.alibaba.fastjson.JSON;
import com.songzhen.howcool.model.enums.RetCodeEnum;
import com.songzhen.howcool.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 預處理回調方法,實現處理器的預處理(如檢查登陸),第三個參數為響應的處理器,自定義Controller
     * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {

        // 判斷對象是否是映射到一個方法,如果不是則直接通過
        if (!(object instanceof HandlerMethod)) {
            // instanceof運算符是用來在運行時指出對象是否是特定類的一個實例
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //檢查方法是否有NeedLogin注解,無則跳過認證
        if (method.isAnnotationPresent(NeedLogin.class)) {
            NeedLogin needLogin = method.getAnnotation(NeedLogin.class);
            if (needLogin.required()) {
                // 從HTTP請求頭中獲取TOKEN信息
                String token = httpServletRequest.getHeader("Authorization");

                // HTTP請求頭中TOKEN解析出的用戶信息
                String uid = JwtUtil.getUid(token);
                String userName = JwtUtil.getUserName(token);
                String realName = JwtUtil.getRealName(token);

                // 檢查TOKEN
                if (!checkToken(token)) {
                    // TOKEN錯誤時,提示用戶登錄
                    Map<String, Object> retMap = new HashMap<>(16);
                    retMap.put("code", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getCode());
                    retMap.put("msg", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getDesc());
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    httpServletResponse.getWriter().append(JSON.toJSONString(retMap));
                    return false;
                }

                // 組裝用戶信息到REQUEST中
                Map<String, Object> currentUser = new HashMap<>(16);
                currentUser.put("uid", uid);
                currentUser.put("userName", userName);
                currentUser.put("realName", realName);
                httpServletRequest.setAttribute("currentUser", currentUser);

                return true;
            }
        }
        return true;
    }

    /**
     * 后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, ModelAndView modelAndView) throws Exception {
        long now = System.currentTimeMillis();
    }

    /**
     * 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }

    /**
     * 檢查TOKEN
     *
     * @param token token
     *              要校驗的token
     * @return boolean
     * true:通過 false:失敗
     */
    private boolean checkToken(String token) {
        // ------------------------認證------------開始-----------------
        if (null == token) {
            return false;
        }

        // 獲取TOKEN中的用戶信息
        String uid = JwtUtil.getUid(token);

        // 根據uid從redis中獲取用戶tokenInRedis
        String tokenInRedis = redisTemplate.opsForValue().get(uid);
        if (null == tokenInRedis) {
            // 如果REDIS異常,返回成功保證正常業務可以繼續處理
            return true;
        }

        // HTTP請求頭中TOKEN解析出的用戶信息
        String userName = JwtUtil.getUserName(token);
        String realName = JwtUtil.getRealName(token);
        String deviceId = JwtUtil.getDeviceId(token);
        long expireIn = JwtUtil.getExpireIn(token);
        // REDIS服務器中TOKEN解析出的用戶信息
        String userNameInRedis = JwtUtil.getUserName(tokenInRedis);
        String realNameInRedis = JwtUtil.getRealName(tokenInRedis);
        String deviceIdInRedis = JwtUtil.getDeviceId(tokenInRedis);
        long expireInInRedis = JwtUtil.getExpireIn(tokenInRedis);

        if (null == userName || null == realName || null == deviceId) {
            return false;
        }
        if (null == userNameInRedis || null == realNameInRedis || null == deviceIdInRedis) {
            return false;
        }
        // 判斷TOKEN是否過期
        if (expireIn != expireInInRedis) {
            return false;
        }
        if (expireIn < System.currentTimeMillis()) {
            return false;
        }
        // ------------------------認證------------結束-----------------
        return true;
    }

}
View Code

3.開發攔截器配置類

package com.songzhen.howcool.config;

import com.songzhen.howcool.auth.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}
View Code

4.在Controller中需要登錄才能操作的方法上加上注解NeedLogin

package com.songzhen.howcool.controller;

import com.songzhen.howcool.entity.QueryUserEntity;
import com.songzhen.howcool.entity.UserLoginEntity;
import com.songzhen.howcool.auth.NeedLogin;
import com.songzhen.howcool.biz.UserBizService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 用戶相關.
 * <p>用戶相關<br>
 * 用戶相關
 *
 * @author Lucas
 * @date 2018/8/9
 */
@RestController
@RequestMapping("**/v1/user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserBizService userBizService;

    /**
     * 登錄
     */
    @PostMapping("login")
    public Map<String, Object> login(@RequestBody UserLoginEntity userLoginEntity) {
        logger.info("login input params userLoginEntity={}", userLoginEntity);
        return userBizService.login(userLoginEntity.getUserName(), userLoginEntity.getPassword(), userLoginEntity.getDeviceId());
    }


    /**
     * 登出
     *
     * @param request request
     */
    @NeedLogin
    @PostMapping("logout")
    public Map<String, Object> logout(HttpServletRequest request) {
        Map<String,Object> currentUser = (Map<String,Object>)request.getAttribute("currentUser");

        return userBizService.logout(currentUser.get("uid").toString());
    }

    /**
     * 列表用戶
     */
    @NeedLogin
    @PostMapping("pageUser")
    public Map<String, Object> pageUser(@RequestBody QueryUserEntity queryUserEntity) {
        logger.info("login input params queryUserEntity={}", queryUserEntity);
        return userBizService.pageUsers(queryUserEntity);
    }

}
View Code

5.GitHub上源代碼地址

https://github.com/songzhen110/howcool.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM