最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot 整合 JWT + Redis 實(shí)現(xiàn)登錄鑒權(quán)

 更新時(shí)間:2026年02月06日 10:02:00   作者:造輪子的豬  
本文主要介紹了SpringBoot 整合 JWT + Redis 實(shí)現(xiàn)登錄鑒權(quán),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、方案說明

本方案采用 JWT + Redis 組合實(shí)現(xiàn)登錄鑒權(quán),解決了純JWT無法主動(dòng)失效、無法續(xù)期的痛點(diǎn):

  1. JWT 生成令牌,承載用戶核心信息,客戶端請(qǐng)求攜帶令牌實(shí)現(xiàn)無狀態(tài)認(rèn)證
  2. Redis 存儲(chǔ)有效令牌,做雙重校驗(yàn)(JWT簽名有效性+Redis令牌存在性),支持令牌主動(dòng)失效(如登出)
  3. 實(shí)現(xiàn)令牌自動(dòng)續(xù)期:令牌剩余有效期不足1/3時(shí),自動(dòng)刷新Redis過期時(shí)間
  4. 登錄接口 做防 暴 力 破解:連續(xù)5次登錄失敗,賬戶鎖定15分鐘,保障賬戶安全
  5. 基于SpringMVC的HandlerInterceptor實(shí)現(xiàn)全局請(qǐng)求攔截,統(tǒng)一校驗(yàn)令牌

二、核心依賴引入

<!-- SpringSecurity 加密/核心工具 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-crypto</artifactId>
    <version>5.7.3</version>
</dependency>

<!-- JJWT 核心依賴 - JWT令牌生成/解析 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

<!-- SpringBoot Redis 啟動(dòng)器 - 存儲(chǔ)有效令牌/續(xù)期控制 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

三、全局請(qǐng)求攔截器 - JwtInterceptor

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Resource
    private JwtServiceImpl jwtService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 獲取請(qǐng)求頭中的令牌
        String authToken = request.getHeader("Authorization");
        
        // 2. 令牌為空,直接返回未授權(quán)
        if (org.springframework.util.StringUtils.isBlank(authToken)) {
            return writeErrorResponse(response, "Token不能為空");
        }

        // 3. 校驗(yàn)令牌格式是否包含Bearer前綴
        if (!authToken.startsWith("Bearer ")) {
            return writeErrorResponse(response, "Token格式錯(cuò)誤,必須以Bearer 開頭");
        }

        // 4. 截取純token字符串
        String token = authToken.substring("Bearer".length() + 1).trim();

        try {
            // 5. 第一步校驗(yàn):Redis中是否存在該令牌,不存在=過期/已注銷/非法令牌
            if (!jwtService.redisHasToken(token)) {
                return writeErrorResponse(response, "Token無效或已過期");
            }

            // 6. 第二步校驗(yàn):解析JWT令牌,獲取載荷信息
            io.jsonwebtoken.Claims claims = jwtService.extractAllClaims(token);
            String userId = claims.getSubject();

            // 7. 第三步校驗(yàn):令牌簽名+用戶信息有效性校驗(yàn)
            boolean isTokenValid = jwtService.validAuthToken(token, userId);
            if (!isTokenValid) {
                return writeErrorResponse(response, "Token無效或已過期");
            }

            // 8. 令牌有效,判斷是否需要自動(dòng)續(xù)期(剩余時(shí)間不足1/3則續(xù)期)
            if (jwtService.isAuthTokenExpiringSoon(token)) {
                jwtService.expireToken(token);
            }

            // 9. 將用戶ID存入ThreadLocal,供后續(xù)業(yè)務(wù)邏輯獲取,無需重復(fù)解析
            UserContext.set(Integer.parseInt(userId));
        } catch (Exception e) {
            // 捕獲所有令牌異常:解析失敗、簽名篡改、數(shù)據(jù)異常等
            return writeErrorResponse(response, "Token無效或已過期");
        }
        // 全部校驗(yàn)通過,放行請(qǐng)求
        return true;
    }

    /**
     * 抽離公共的錯(cuò)誤響應(yīng)方法,統(tǒng)一返回JSON格式錯(cuò)誤信息
     */
    private boolean writeErrorResponse(HttpServletResponse response, String msg) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        String errorJson = "{\"code\": 401, \"message\": \"" + msg + "\"}";
        response.getWriter().print(errorJson);
        return false;
    }
}

四、JWT核心業(yè)務(wù)實(shí)現(xiàn)類 - JwtServiceImpl

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Base64;

@Service
public class JwtServiceImpl {

    @Resource
    private RedisService redisService;

    // ======================== 常量配置區(qū) ========================
    /** redis中令牌的前綴 */
    private static final String TOKEN_AUTH_PREFIX = "token:auth:";
    /** JWT簽名密鑰【生產(chǎn)環(huán)境請(qǐng)改為32位以上隨機(jī)字符串,Base64編碼后的值】 */
    private static final String SECRET_KEY = Base64.getEncoder().encodeToString("springboot-jwt-redis-auth-2026-key".getBytes());
    /** JWT令牌默認(rèn)有效期 單位:分鐘 */
    private static final Integer DEFAULT_AUTH_EXPIRE_MINUTE = 30;
    /** JWT令牌有效期配置 單位:分鐘 */
    private static final Integer JWT_TOKEN_EXPIRE_MINUTE = 30;

    // ======================== 私有工具方法 ========================
    /**
     * 獲取JWT簽名密鑰對(duì)象,基于HS256算法
     */
    private Key getSigningKey() {
        byte[] keyBytes = Base64.getDecoder().decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 獲取Redis中令牌的剩余過期時(shí)間,單位:秒
     */
    private long getTokenRedisExpire(String token) {
        return redisService.getExpire(TOKEN_AUTH_PREFIX + token);
    }

    /**
     * 獲取令牌續(xù)期閾值:剩余有效期不足總時(shí)長(zhǎng)的1/3時(shí),觸發(fā)自動(dòng)續(xù)期
     */
    private long getAuthRefreshSeconds() {
        return getAuthExpireSeconds() / 3;
    }

    // ======================== 公有對(duì)外方法 ========================
    /**
     * 計(jì)算令牌有效期,轉(zhuǎn)成秒數(shù)返回
     */
    public int getAuthExpireSeconds() {
        Integer expireMinutes = Optional.ofNullable(JWT_TOKEN_EXPIRE_MINUTE).orElse(DEFAULT_AUTH_EXPIRE_MINUTE);
        return expireMinutes * 60;
    }

    /**
     * 構(gòu)建JWT令牌,自定義載荷信息
     * @param identifier 主題,存儲(chǔ)用戶ID等唯一標(biāo)識(shí)
     * @param claimsMap  自定義載荷,可存儲(chǔ)用戶角色、權(quán)限等信息
     * @param secondsValidity 令牌有效期,單位:秒
     * @return 生成的JWT令牌字符串
     */
    public String generateTokenWithClaims(String identifier, Map<String, Object> claimsMap, int secondsValidity) {
        return Jwts.builder()
                .setClaims(claimsMap)
                .setSubject(identifier)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + secondsValidity * 1000))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 解析token,獲取自定義載荷中的purpose字段
     */
    public String extractPurpose(String token) {
        try {
            return extractAllClaims(token).get("purpose", String.class);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解析token,獲取全部載荷信息
     * @throws ExpiredJwtException token過期
     * @throws SignatureException 簽名錯(cuò)誤/令牌篡改
     * @throws MalformedJwtException 令牌格式錯(cuò)誤
     */
    public Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 生成登錄鑒權(quán)專用令牌
     * @param userId 用戶ID
     * @return JWT令牌
     */
    public String generateAuthToken(Integer userId) {
        // 設(shè)置令牌用途和業(yè)務(wù)類型,便于后續(xù)擴(kuò)展多場(chǎng)景令牌
        Map<String, Object> claimsMap = generateClaims("auth", "accessControl");
        // 生成JWT令牌
        String token = generateTokenWithClaims(String.valueOf(userId), claimsMap, getAuthExpireSeconds());
        // 令牌存入Redis,Redis過期時(shí)間與JWT一致,雙重保障
        redisService.set(TOKEN_AUTH_PREFIX + token, String.valueOf(userId), getAuthExpireSeconds());
        return token;
    }

    /**
     * 校驗(yàn)令牌有效性:Redis存在性+用戶ID一致性校驗(yàn)
     */
    public boolean validAuthToken(String token, String userId) {
        String storedUserId = getAuthData(token);
        return StringUtils.isNotBlank(storedUserId) && userId.equals(storedUserId);
    }

    /**
     * 判斷令牌是否即將過期,是否需要續(xù)期
     */
    public boolean isAuthTokenExpiringSoon(String token) {
        long remainExpireSeconds = getTokenRedisExpire(token);
        long refreshThreshold = getAuthRefreshSeconds();
        return remainExpireSeconds > 0 && remainExpireSeconds < refreshThreshold;
    }

    /**
     * 令牌續(xù)期:刷新Redis中令牌的過期時(shí)間,實(shí)現(xiàn)無感續(xù)期
     */
    public void expireToken(String token){
        redisService.expireKey(TOKEN_AUTH_PREFIX + token, getAuthExpireSeconds());
    }

    /**
     * 獲取Redis中存儲(chǔ)的令牌綁定的用戶ID
     */
    public String getAuthData(String token){
        return redisService.get(TOKEN_AUTH_PREFIX + token);
    }

    /**
     * 構(gòu)建JWT自定義載荷信息
     */
    public Map<String, Object> generateClaims(String purpose, String busType) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("purpose", purpose);
        claims.put("busType", busType);
        return claims;
    }

    /**
     * 判斷Redis中是否存在該令牌
     */
    public boolean redisHasToken(String token){
        return redisService.hasKey(TOKEN_AUTH_PREFIX + token);
    }
}

五、登錄業(yè)務(wù)實(shí)現(xiàn)

import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class LoginServiceImpl {

    @Resource
    private UserService userService;
    @Resource
    private JwtServiceImpl jwtService;

    /** 登錄失敗鎖定閾值:連續(xù)失敗5次 */
    private static final Integer FAILED_LOCK_COUNT = 5;
    /** 賬戶鎖定時(shí)長(zhǎng):15分鐘,單位毫秒 */
    private static final Long LOCK_TIME = 15 * 60 * 1000L;

    /**
     * 用戶登錄核心方法
     * @param userDto 登錄入?yún)ⅲㄓ脩裘?密碼)
     * @return 登錄成功返回JWT令牌,失敗拋出業(yè)務(wù)異常
     */
    public String login(UserDto userDto){
        // 1. 根據(jù)用戶名查詢用戶,用戶不存在直接返回失敗
        User user = userService.getByUsername(userDto.getUsername());
        if (user == null) {
            throw new BusinessException("用戶名或密碼錯(cuò)誤");
        }

        boolean needResetStatus = false;
        Integer failedAttempts = user.getFailedAttempts();
        Short userStatus = user.getStatus();

        // 2. 判斷賬戶是否被鎖定(連續(xù)5次失?。?
        if (FAILED_LOCK_COUNT.equals(failedAttempts)) {
            long lastFailTime = user.getUpdatedDate().getTime();
            // 判斷是否超過鎖定時(shí)間
            if (isTimeExceeded(lastFailTime, LOCK_TIME)) {
                // 鎖定時(shí)間已過,重置失敗次數(shù)和狀態(tài)
                needResetStatus = true;
            } else {
                // 賬戶仍在鎖定中
                throw new BusinessException("連續(xù)5次登錄失敗,賬戶已鎖定15分鐘,請(qǐng)稍后再試");
            }
        }

        // 3. 判斷用戶狀態(tài)是否正常
        if (!UserEnum.NORMAL.getStatus().equals(userStatus) && !needResetStatus) {
            throw new BusinessException("賬戶狀態(tài)異常,無法登錄");
        }

        // 4. 校驗(yàn)密碼是否正確
        boolean passwordValid = userService.verifyPassword(userDto.getPassword(), user.getPassword());
        if (passwordValid) {
            // 密碼正確:重置失敗次數(shù)+解鎖賬戶
            if (needResetStatus) {
                userService.updateUserStatus(user.getId());
            }
            // 生成并返回令牌
            return jwtService.generateAuthToken(user.getId());
        } else {
            // 密碼錯(cuò)誤:失敗次數(shù)+1,達(dá)到閾值則鎖定
            userService.incrFailedAttempts(userDto.getUsername());
            throw new BusinessException("用戶名或密碼錯(cuò)誤");
        }
    }

    /**
     * 判斷是否超過指定時(shí)長(zhǎng)
     * @param startTime 開始時(shí)間戳
     * @param timeLimit 時(shí)長(zhǎng)限制(毫秒)
     */
    private boolean isTimeExceeded(long startTime, long timeLimit) {
        return System.currentTimeMillis() - startTime > timeLimit;
    }
}

六、攔截器和ThreadLocal

6.1 攔截器注冊(cè)配置類

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                // 攔截所有請(qǐng)求
                .addPathPatterns("/**")
                // 放行登錄接口、靜態(tài)資源等無需鑒權(quán)的接口
                .excludePathPatterns("/user/login", "/error");
    }
}

6.2 ThreadLocal用戶上下文 - UserContext

public class UserContext {
    private static final ThreadLocal<Integer> USER_ID_CONTEXT = new ThreadLocal<>();

    /** 設(shè)置當(dāng)前線程的用戶ID */
    public static void set(Integer userId) {
        USER_ID_CONTEXT.set(userId);
    }

    /** 獲取當(dāng)前線程的用戶ID */
    public static Integer get() {
        return USER_ID_CONTEXT.get();
    }

    /** 清除當(dāng)前線程的用戶ID,防止內(nèi)存泄漏 */
    public static void remove() {
        USER_ID_CONTEXT.remove();
    }
}

七、核心亮點(diǎn)

  1. 高安全性:JWT簽名防篡改 + Redis雙重校驗(yàn) + 賬戶防暴力破解鎖定,三重保障
  2. 高可用性:令牌自動(dòng)無感續(xù)期,用戶無需重復(fù)登錄,體驗(yàn)友好
  3. 高健壯性:完善的異常處理,避免token格式錯(cuò)誤、篡改、過期導(dǎo)致的程序崩潰
  4. 高可維護(hù)性:代碼結(jié)構(gòu)清晰,常量統(tǒng)一管理,注釋完整,邏輯精簡(jiǎn)

生產(chǎn)環(huán)境必改配置

  1. SECRET_KEY:必須改為32位以上的隨機(jī)字符串,Base64編碼后使用,防止密鑰被破解
  2. 令牌有效期:可根據(jù)業(yè)務(wù)調(diào)整,建議后臺(tái)管理系統(tǒng)30分鐘,移動(dòng)端2小時(shí)
  3. Redis部署:生產(chǎn)環(huán)境建議使用Redis集群,防止單點(diǎn)故障
  4. 密碼加密:必須使用BCryptPasswordEncoder加密存儲(chǔ),禁止明文存儲(chǔ)

核心設(shè)計(jì)思想

為什么用JWT+Redis,而不是純JWT/純Redis?

  • 純JWT:令牌一旦生成無法主動(dòng)失效,過期時(shí)間固定,續(xù)期困難
  • 純Redis:需要存儲(chǔ)大量用戶信息,Redis壓力大,且無狀態(tài)認(rèn)證優(yōu)勢(shì)喪失
  • JWT+Redis:揚(yáng)長(zhǎng)避短,JWT做無狀態(tài)令牌,Redis做有效令牌存儲(chǔ)+續(xù)期,完美解決痛點(diǎn)

到此這篇關(guān)于SpringBoot 整合 JWT + Redis 實(shí)現(xiàn)登錄鑒權(quán)的文章就介紹到這了,更多相關(guān)SpringBoot JWT Redis登錄鑒權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java基于ProcessBuilder+FFmpeg實(shí)現(xiàn)MP4轉(zhuǎn)WAV音頻轉(zhuǎn)碼方案

    Java基于ProcessBuilder+FFmpeg實(shí)現(xiàn)MP4轉(zhuǎn)WAV音頻轉(zhuǎn)碼方案

    在音視頻處理場(chǎng)景中,將MP4視頻文件中的音頻流提取為WAV格式是一個(gè)常見需求,本文介紹一種基于Java ProcessBuilder + FFmpeg的高效解決方案,通過調(diào)用命令行工具FFmpeg完成轉(zhuǎn)換,實(shí)現(xiàn)跨平臺(tái)、高質(zhì)量的音頻提取,需要的朋友可以參考下
    2026-03-03
  • Java使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏

    Java使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏

    我們?cè)谑褂檬謾C(jī)銀行的時(shí)候經(jīng)常能看到APP上會(huì)將銀行卡的卡號(hào)中間部分給隱藏掉使用 ***** 來代替,在某些網(wǎng)站上查看一些業(yè)務(wù)密碼時(shí)(例如簽到密碼等)也會(huì)使用 ***** 來隱藏掉真正的密碼,那么這種方式是如何實(shí)現(xiàn)的呢,本文將給大家介紹使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏
    2023-09-09
  • mybatis-plus之自動(dòng)映射字段(typeHandler)的注意點(diǎn)及說明

    mybatis-plus之自動(dòng)映射字段(typeHandler)的注意點(diǎn)及說明

    這篇文章主要介紹了mybatis-plus之自動(dòng)映射字段(typeHandler)的注意點(diǎn)及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • java多線程實(shí)現(xiàn)文件下載

    java多線程實(shí)現(xiàn)文件下載

    這篇文章主要為大家詳細(xì)介紹了java多線程實(shí)現(xiàn)文件下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Spring4如何自定義@Value功能詳解

    Spring4如何自定義@Value功能詳解

    這篇文章主要給大家介紹了關(guān)于Spring4如何自定義@Value功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring4具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • java的Builder原理和實(shí)現(xiàn)詳解

    java的Builder原理和實(shí)現(xiàn)詳解

    大家好,本篇文章主要講的是java的Builder原理和實(shí)現(xiàn)詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Java正則表達(dá)式如何匹配特定html標(biāo)簽內(nèi)的內(nèi)容

    Java正則表達(dá)式如何匹配特定html標(biāo)簽內(nèi)的內(nèi)容

    這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式如何匹配特定html標(biāo)簽內(nèi)的內(nèi)容的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件方法記錄

    Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件方法記錄

    最近好像得罪了poi,遇到的都是導(dǎo)出word、Excel、pdf的問題,下面這篇文章主要給大家介紹了關(guān)于Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • 學(xué)習(xí)不同 Java.net 語(yǔ)言中類似的函數(shù)結(jié)構(gòu)

    學(xué)習(xí)不同 Java.net 語(yǔ)言中類似的函數(shù)結(jié)構(gòu)

    這篇文章主要介紹了學(xué)習(xí)不同 Java.net 語(yǔ)言中類似的函數(shù)結(jié)構(gòu),函數(shù)式編程語(yǔ)言包含多個(gè)系列的常見函數(shù)。但開發(fā)人員有時(shí)很難在語(yǔ)言之間進(jìn)行切換,因?yàn)槭煜さ暮瘮?shù)具有不熟悉的名稱。函數(shù)式語(yǔ)言傾向于基于函數(shù)范例來命名這些常見函數(shù)。,需要的朋友可以參考下
    2019-06-06
  • windows 32位eclipse遠(yuǎn)程hadoop開發(fā)環(huán)境搭建

    windows 32位eclipse遠(yuǎn)程hadoop開發(fā)環(huán)境搭建

    這篇文章主要介紹了windows 32位eclipse遠(yuǎn)程hadoop開發(fā)環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下
    2016-07-07

最新評(píng)論

南川市| 左贡县| 铜梁县| 潢川县| 滕州市| 泉州市| 盱眙县| 天峨县| 桃江县| 邵武市| 垦利县| 汉阴县| 梁山县| 察哈| 宁乡县| 宁晋县| 洪江市| 正镶白旗| 陵水| 娄底市| 威宁| 泗洪县| 金川县| 南投市| 太谷县| 东台市| 闽侯县| 黄冈市| 淳化县| 诸城市| 咸宁市| 定安县| 米泉市| 乌苏市| 托里县| 板桥市| 南投县| 华阴市| 曲水县| 德保县| 新兴县|