1. 程式人生 > >SpringBoot中使用 攔截器

SpringBoot中使用 攔截器

在web開發的過程中,為了實現登入許可權驗證,我們往往需要新增一個攔截器在使用者的的請求到達controller層的時候實現登入驗證,那麼SpringBoot如何新增攔截器呢?

步驟如下:

1.繼承WebMvcConfigureAdapter類,覆蓋其addInterceptors介面,註冊我們自定義的攔截器:

/** 
 * 
 * 註冊攔截器 
 * Created by SYSTEM on 2017/8/16. 
 */  
public class WebAppConfig extends WebMvcConfigurerAdapter {  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        //註冊自定義攔截器,新增攔截路徑和排除攔截路徑  
        registry.addInterceptor(new InterceptorConfig()).addPathPatterns("api/path/**").excludePathPatterns("api/path/login");  
    }  
} 

2.實現HandlerInterceptor介面,重寫介面中的三個方法:

public class InterceptorConfig  implements HandlerInterceptor{  
    private static final Logger log = LoggerFactory.getLogger(InterceptorConfig.class);  
    /** 
     * 進入controller層之前攔截請求 
     * @param httpServletRequest 
     * @param httpServletResponse 
     * @param o 
     * @return 
     * @throws Exception 
     */  
    @Override  
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {  
  
        log.info("---------------------開始進入請求地址攔截--- 從session獲取資料如果存在就放行不存在就丟擲異常攔截-------------------------");  
        HttpSession session = httpServletRequest.getSession();  
        if(!StringUtils.isEmpty(session.getAttribute("userName"))){  
            return true;  
        }  
        else{  
            PrintWriter printWriter = httpServletResponse.getWriter();  
            printWriter.write("{code:0,message:\"session is invalid,please login again!\"}");  
            return false;  
        } 
    }  
  
    @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");  
    }  
}  

註冊攔截器

@Configuration
public class MyWebConfig extends WebMvcConfigurerAdapter{

    /**
     * 註冊 攔截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogHandlerInterceptor());
    }

}
測試:這樣我們就可以在使用者請求到達controller層實現登入攔截了,所有使用者請求都會被攔截,在prehandle方法進行登入判斷,返回true則驗證通過,否則失敗

UserController

@RestController
public class UserController {

    @GetMapping("/user/home")
    public String home(){
        System.out.println("--- user home ---");
        return "user home";
    }

}


redis==========================

1、引入 spring-boot-starter-redis(1.4版本前),spring-boot-starter-data-redis(1.4版本後)

        <!--  cache -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--  redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2,application.properties 配置檔案

# Redis資料庫索引(預設為0)
spring.redis.database=0
# Redis伺服器地址
spring.redis.host=localhost
# Redis伺服器連線埠
spring.redis.port=6379
# Redis伺服器連線密碼(預設為空)
spring.redis.password=
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連線池中的最大空閒連線
spring.redis.pool.max-idle=8
# 連線池中的最小空閒連線
spring.redis.pool.min-idle=0
# 連線超時時間(毫秒)
spring.redis.timeout=0

2.SpringBoot配置檔案中配置Redis連線(YAML方式配置)

spring:
    application:
        name: spring-boot-redis
    redis:
        host: 192.168.145.132
        port: 6379
        timeout: 20000
        cluster:
            nodes: 192.168.211.134:7000,192.168.211.134:7001,192.168.211.134:7002
            maxRedirects: 6
        pool:
            max-active: 8
            min-idle: 0
            max-idle: 8
            max-wait: -1

解釋:本配置採用Redis一主三從的的配置方式來提高快取的吞吐量

3.Redis快取配置類提供redisTemplate(獲得配置檔案中連線引數後的)

@Configuration
@EnableCaching
public class RedisCacheConfig {
    @Bean
    public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate){
        CacheManager cacheManager = new RedisCacheManager(redisTemplate);
        return cacheManager;
    }
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String,String>();
        redisTemplate.setConnectionFactory(factory);
        // key序列化方式;(不然會出現亂碼;),但是如果方法上有Long等非String型別的話,會報型別轉換錯誤;
        // 所以在沒有自己定義key生成策略的時候,以下這個程式碼建議不要這麼寫,可以不配置或者自己實現ObjectRedisSerializer
        // 或者JdkSerializationRedisSerializer序列化方式;
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();// Long型別不可以會出現異常資訊;
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        return redisTemplate;
    }
    
}

Redis工具類

/**
 * redicache 工具類
 * 
 */
@SuppressWarnings("unchecked")
@Component
public class RedisUtil {
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
/**
     * 寫入快取
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 寫入快取設定時效時間
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 批量刪除對應的value
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量刪除key
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0)
            redisTemplate.delete(keys);
    }
    /**
     * 刪除對應的value
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }
    /**
     * 判斷快取中是否有對應的value
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 讀取快取
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }
    /**
     * 雜湊 新增
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value){
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key,hashKey,value);
    }

    /**
     * 雜湊獲取資料
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey){
        HashOperations<String, Object, Object>  hash = redisTemplate.opsForHash();
        return hash.get(key,hashKey);
    }

    /**
     * 列表新增
     * @param k
     * @param v
     */
    public void lPush(String k,Object v){
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k,v);
    }

    /**
     * 列表獲取
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1){
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k,l,l1);
    }

    /**
     * 集合新增
     * @param key
     * @param value
     */
    public void add(String key,Object value){
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key,value);
    }

    /**
     * 集合獲取
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key){
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合新增
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key,Object value,double scoure){
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key,value,scoure);
    }

    /**
     * 有序集合獲取
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key,double scoure,double scoure1){
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }
}

 5.使用方法 測試用的Controller
import com.example.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    @RequestMapping(value = "/test",method = RequestMethod.POST)
    public void demoTest(){
        
RedisUtil.set("1","value22222");
}}

將使用者資訊儲存到redis中

1.繼承WebMvcConfigureAdapter類,覆蓋其addInterceptors介面,註冊我們自定義的攔截器:

@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {

    @Bean
    public HandlerInterceptor getMyInterceptor(){
        return new MyHandlerInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns 用於新增攔截規則, 這裡假設攔截 /url 後面的全部連結
        // excludePathPatterns 使用者排除攔截
        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/api/*");
        super.addInterceptors(registry);
    }


}

2.實現HandlerInterceptor介面,重寫介面中的三個方法:

public class MyHandlerInterceptor implements HandlerInterceptor {
    private final Logger logger = LoggerFactory.getLogger(MyHandlerInterceptor.class);

    @Autowired
    private RedConf conf;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) httpServletResponse;
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        boolean retMsg = false;
        String token = httpServletRequest.getHeader("token");
        if (null == token || token.isEmpty()) {
            //json 形式返回前端
            JSONObject res = new JSONObject();
            res.put("success", "false");
            res.put("msg", "token沒有認證通過!原因為:客戶端請求引數中無token資訊");
            out = httpResponse.getWriter();
            out.append(res.toString());
            logger.info("token沒有認證通過!原因為:客戶端請求引數中無token資訊");
            return retMsg;
        } else {
            Jedis jedis = RedisUtil.getJedis(conf.getRedisIp(),Integer.parseInt(conf.getRedisPort()),1000,conf.getRedisAuth());
            boolean exits = jedis.exists(token);
            if (exits) {
                jedis.expire(token, 900);
                retMsg = true;
                logger.info("認證成功");
                RedisUtil.returnResource(jedis);
                return retMsg;
            } else {
                JSONObject res = new JSONObject();
                res.put("success", "false");
                res.put("msg", "當前的token已過期,請重新登陸");
                out = httpResponse.getWriter();
                out.append(res.toString());
                logger.info("當前的token已過期,請重新登陸");
                return retMsg;
            }
        }
    }

    @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 {

    }
}