SpringBoot+JWT+Redis实现单点登录功能

正文

大家可上我的git上下载源码,可以的话请给个星星,谢谢了。

代码结构图

在这里插入图片描述

代码,从上往下

AESSecretUtil:

package com.jwt.demo;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

/**
 * @author : HJH
 * @Description : AES加密工具类
 * @date  2019/8/2 15:27
 */
public class AESSecretUtil {

    /**
     * 秘钥的大小
     */
    private static final int KEYSIZE = 128;

    /**
     * @param data  待加密内容
     * @param key   加密秘钥
     * @deprecated  AES加密
     * @return 加密后的数组
     */
    public static byte[] encrypt(String data, String key) {
        if (StringUtils.isNotBlank(data)) {
            try {
                KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
                //选择一种固定算法,为了避免不同java实现的不同算法,生成不同的密钥,而导致解密失败
                SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                random.setSeed(key.getBytes());
                keyGenerator.init(KEYSIZE, random);
                SecretKey secretKey = keyGenerator.generateKey();
                byte[] enCodeFormat = secretKey.getEncoded();
                SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
                Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                byte[] byteContent = data.getBytes("utf-8");
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 初始化
                byte[] result = cipher.doFinal(byteContent);
                return result; // 加密
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * @param data 待加密内容
     * @param key  加密秘钥
     * @deprecated AES加密
     * @return 返回String
     */
    public static String encryptToStr(String data, String key) {

        return StringUtils.isNotBlank(data) ? parseByte2HexStr(encrypt(data, key)) : null;
    }


    /**
     * @param data - 待解密字节数组
     * @param key  - 秘钥
     * @deprecated : AES解密
     */
    public static byte[] decrypt(byte[] data, String key) {
        if (ArrayUtils.isNotEmpty(data)) {
            try {
                KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
                //选择一种固定算法,为了避免不同java实现的不同算法,生成不同的密钥,而导致解密失败
                SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                random.setSeed(key.getBytes());
                keyGenerator.init(KEYSIZE, random);
                SecretKey secretKey = keyGenerator.generateKey();
                byte[] enCodeFormat = secretKey.getEncoded();
                SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
                Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化
                byte[] result = cipher.doFinal(data);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * @param enCryptdata  待解密字节数组
     * @param key          秘钥
     * @deprecated : AES解密
     * @return 返回String
     */
    public static String decryptToStr(String enCryptdata, String key) {
        return StringUtils.isNotBlank(enCryptdata) ? new String(decrypt(parseHexStr2Byte(enCryptdata), key)) : null;
    }

    /**
     * @param buf - 二进制数组
     * @deprecated : 将二进制转换成16进制
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * @param hexStr - 16进制字符串
     * @deprecated : 将16进制转换为二进制
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1) {
            return null;
        }
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    public static void main(String[] args) {
        String ss = encryptToStr("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMiLCJ1c2VyTmFtZSI6Ikp1ZHkiLCJleHAiOjE1MzI3Nzk2MjIsIm5iZiI6MTUzMjc3NzgyMn0.sIw_leDZwG0pJ8ty85Iecd_VXjObYutILNEwPUyeVSo", SecretConstant.DATAKEY);
        System.out.println(ss);
        System.out.println(decryptToStr(ss, SecretConstant.DATAKEY));
    }

}

Base64Util

package com.jwt.demo;


/**
 * @author : HJH
 * @Description : Base64转码解码工具类
 * @date : 2019/8/2 15:27
 */
public class Base64Util {

    //字母表
    private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();

    private static byte[] codes = new byte[256];

    static {
        for (int i = 0; i < 256; i++) {
            codes[i] = -1;
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            codes[i] = (byte) (i - 'A');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }

        for (int i = 'a'; i <= 'z'; i++) {
            codes[i] = (byte) (26 + i - 'a');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        for (int i = '0'; i <= '9'; i++) {
            codes[i] = (byte) (52 + i - '0');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        codes['+'] = 62;
        codes['/'] = 63;
    }

    /**
     * @param data
     * @deprecated : 功能:编码字符串
     */
    public static String encode(String data) {
        return new String(encode(data.getBytes()));
    }

    /**
     * @param data
     * @deprecated : 功能:解码字符串
     */
    public static String decode(String data) {
        return new String(decode(data.toCharArray()));
    }

    /**
     * @param data - 字节数组
     * @deprecated : 功能:编码byte[]
     */
    public static char[] encode(byte[] data) {
        char[] out = new char[((data.length + 2) / 3) * 4];
        for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
            boolean quad = false;
            boolean trip = false;

            int val = (0xFF & (int) data[i]);
            val <<= 8;
            if ((i + 1) < data.length) {
                val |= (0xFF & (int) data[i + 1]);
                trip = true;
            }
            val <<= 8;
            if ((i + 2) < data.length) {
                val |= (0xFF & (int) data[i + 2]);
                quad = true;
            }
            out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 1] = alphabet[val & 0x3F];
            val >>= 6;
            out[index + 0] = alphabet[val & 0x3F];
        }
        return out;
    }

    /**
     * @param data - 字节数组
     * @deprecated : 功能:解码字节数组
     */
    public static byte[] decode(char[] data) {

        int tempLen = data.length;
        for (int ix = 0; ix < data.length; ix++) {
            if ((data[ix] > 255) || codes[data[ix]] < 0) {
                --tempLen; // ignore non-valid chars and padding
            }
        }
        // calculate required length:
        // -- 3 bytes for every 4 valid base64 chars
        // -- plus 2 bytes if there are 3 extra base64 chars,
        // or plus 1 byte if there are 2 extra.

        int len = (tempLen / 4) * 3;
        if ((tempLen % 4) == 3) {
            len += 2;
        }
        if ((tempLen % 4) == 2) {
            len += 1;

        }
        byte[] out = new byte[len];

        int shift = 0; // # of excess bits stored in accum
        int accum = 0; // excess bits
        int index = 0;

        // we now go through the entire array (NOT using the 'tempLen' value)
        for (int ix = 0; ix < data.length; ix++) {
            int value = (data[ix] > 255) ? -1 : codes[data[ix]];

            if (value >= 0) { // skip over non-code
                accum <<= 6; // bits shift up by 6 each time thru
                shift += 6; // loop, with new bits being put in
                accum |= value; // at the bottom.
                if (shift >= 8) { // whenever there are 8 or more shifted in,
                    shift -= 8; // write them out (from the top, leaving any
                    out[index++] = // excess at the bottom for next iteration.
                            (byte) ((accum >> shift) & 0xff);
                }
            }
        }

        // if there is STILL something wrong we just have to throw up now!
        if (index != out.length) {
            throw new Error("Miscalculated data length (wrote " + index
                    + " instead of " + out.length + ")");
        }

        return out;
    }

}

JwtHelper

package com.jwt.demo;

import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author  HJH
 * @Description : JWT工具类
 * <br/>参考官网:https://jwt.io/
 * <br/>JWT的数据结构为:A.B.C三部分数据,由字符点"."分割成三部分数据
 * <br/>A-header头信息
 * <br/>B-payload 有效负荷 一般包括:已注册信息(registered claims),公开数据(public claims),私有数据(private claims)
 * <br/>C-signature 签名信息 是将header和payload进行加密生成的
 * @date  2019/8/2 15:27
 */
public class JwtHelper {

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

    /**
     * @param userId     - 用户编号
     * @param userName   - 用户名
     * @param identities - 客户端信息(变长参数),目前包含浏览器信息,用于客户端拦截器校验,防止跨域非法访问
     * @Author: Helon
     * @deprecated  生成JWT字符串
     * <br/>格式:A.B.C
     * <br/>A-header头信息
     * <br/>B-payload 有效负荷
     * <br/>C-signature 签名信息 是将header和payload进行加密生成的
     * @Data: 2018/7/28 19:26
     * @Modified By:
     */
    public static String generateJWT(String userId, String userName, String... identities) {
        //签名算法,选择SHA-256
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //获取当前系统时间
        long nowTimeMillis = System.currentTimeMillis();
        Date now = new Date(nowTimeMillis);
        //将BASE64SECRET常量字符串使用base64解码成字节数组
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET);
        //使用HmacSHA256签名算法生成一个HS256的签名秘钥Key
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        //添加构成JWT的参数
        Map<String, Object> headMap = new HashMap<>();
        /*
            Header
            {
              "alg": "HS256",
              "typ": "JWT"
            }
         */
        headMap.put("alg", SignatureAlgorithm.HS256.getValue());
        headMap.put("typ", "JWT");
        JwtBuilder builder = Jwts.builder().setHeader(headMap)
                /*
                    Payload
                    {
                      "userId": "1234567890",
                      "userName": "John Doe",
                    }
                 */
                //加密后的客户编号
                .claim("userId", AESSecretUtil.encryptToStr(userId, SecretConstant.DATAKEY))
                //客户名称
                .claim("userName", userName)
                //客户端浏览器信息
                .claim("userAgent", identities[0])
                //Signature
                .signWith(signatureAlgorithm, signingKey);
        //添加Token过期时间
        if (SecretConstant.EXPIRESSECOND >= 0) {
            long expMillis = nowTimeMillis + SecretConstant.EXPIRESSECOND;
            Date expDate = new Date(expMillis);
            builder.setExpiration(expDate).setNotBefore(now);
        }
        return builder.compact();
    }

    /**
     * @param jsonWebToken - 页面传过来的token
     * @deprecated: 解析JWT
     * @return Claims对象
     */
    public static Claims parseJWT(String jsonWebToken) {
        Claims claims = null;
        try {
            if (StringUtils.isNotBlank(jsonWebToken)) {
                //解析jwt
                claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET))
                        .parseClaimsJws(jsonWebToken).getBody();
            } else {
                logger.warn("[JWTHelper]-json web token 为空");
            }
        } catch (Exception e) {
            logger.error("[JWTHelper]-JWT解析异常:可能因为token已经超时或非法token");
        }
        return claims;
    }

    /**
     * @param jsonWebToken - 页面传过来的token
     * @deprecated: 校验JWT是否有效
     * @return json字符串的demo:<br/>
     * {"freshToken":"A.B.C","userName":"Judy","userId":"123", "userAgent":"xxxx"}
     * <br/>freshToken-刷新后的jwt
     * <br/>userName-客户名称
     * <br/>userId-客户编号
     * <br/>userAgent-客户端浏览器信息
     */
    public static String validateLogin(String jsonWebToken) {
        Map<String, Object> retMap = null;
        Claims claims = parseJWT(jsonWebToken);
        if (claims != null) {
            //解密客户编号
            String decryptUserId = AESSecretUtil.decryptToStr((String) claims.get("userId"), SecretConstant.DATAKEY);
            retMap = new HashMap<>();
            //加密后的客户编号
            retMap.put("userId", decryptUserId);
            //客户名称
            retMap.put("userName", claims.get("userName"));
            //客户端浏览器信息
            retMap.put("userAgent", claims.get("userAgent"));
            //刷新JWT
            retMap.put("freshToken", generateJWT(decryptUserId, (String) claims.get("userName"), (String) claims.get("userAgent"), (String) claims.get("domainName")));
        } else {
            logger.warn("[JWTHelper]-JWT解析出claims为空");
        }
        return retMap != null ? JSONObject.toJSONString(retMap) : null;
    }

    public static void main(String[] args) {
        String jsonWebKey = generateJWT("123", "Judy",
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
        System.out.println(jsonWebKey);
        Claims claims = parseJWT(jsonWebKey);
        System.out.println(claims);
        System.out.println(validateLogin(jsonWebKey));

    }


}

SecretConstant

package com.jwt.demo;

/**
 * @author  HJH
 * @Description  : JWT使用常量值
 * @date  2019/8/2 15:27
 */
public class SecretConstant {

    //签名秘钥
    public static final String BASE64SECRET = "ZW]4l5JH[m6Lm)LaQEjpb!4E0lRaG(";

    //超时毫秒数(默认30分钟)
    public static final int EXPIRESSECOND = 1800000;

    //用于JWT加密的密匙
    public static final String DATAKEY = "u^3y6SPER41jm*fn";

}

ValidateLoginInterceptor

package com.jwt.demo;



import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author HJH
 * @Description : 校验是否登录拦截器
 * @date 2019/8/2 14:45
 */
@Slf4j
public class ValidateLoginInterceptor implements HandlerInterceptor {

    private String SESSION_CUSTOMER_NO_KEY = "session_customer_no_key";

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //首先从请求头中获取jwt串,与页面约定好存放jwt值的请求头属性名为User-Token
        String jwt = httpServletRequest.getHeader("User-Token");
        log.info("[登录校验拦截器]-从header中获取的jwt为:{}", jwt);
        //判断jwt是否有效
        if(StringUtils.isNotBlank(jwt)){
            //校验jwt是否有效,有效则返回json信息,无效则返回空
            String retJson = JwtHelper.validateLogin(jwt);
            log.info("[登录校验拦截器]-校验JWT有效性返回结果:{}", retJson);
            //retJSON为空则说明jwt超时或非法
            if(StringUtils.isNotBlank(retJson)){
                JSONObject jsonObject = JSONObject.parseObject(retJson);
                //校验客户端信息
                String userAgent = httpServletRequest.getHeader("User-Agent");
                if (userAgent.equals(jsonObject.getString("userAgent"))) {
                    //获取刷新后的jwt值,设置到响应头中
                    httpServletResponse.setHeader("User-Token", jsonObject.getString("freshToken"));
                    //将客户编号设置到session中
                    httpServletRequest.getSession().setAttribute(SESSION_CUSTOMER_NO_KEY, jsonObject.getString("userId"));
                    return true;
                }else{
                    log.warn("[登录校验拦截器]-客户端浏览器信息与JWT中存的浏览器信息不一致,重新登录。当前浏览器信息:{}", userAgent);
                }
            }else {
                log.warn("[登录校验拦截器]-JWT非法或已超时,重新登录");
            }
        }
        //输出响应流
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hmac", "");
        jsonObject.put("status", "");
        jsonObject.put("code", "4007");
        jsonObject.put("msg", "未登录");
        jsonObject.put("data", "");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        httpServletResponse.getOutputStream().write(jsonObject.toJSONString().getBytes("UTF-8"));
        return false;
    }

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

    }
}


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jwt</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <java-jwt>3.3.0</java-jwt>
        <jsonwebtoken.jjwt>0.9.0</jsonwebtoken.jjwt>
        <alibaba.fastjson>1.2.47</alibaba.fastjson>
        <apache.commons-lang3>3.7</apache.commons-lang3>
        <slf4j.version>1.6.4</slf4j.version>
        <junit.version>4.12</junit.version>
        <apache.commons-codec>1.11</apache.commons-codec>
        <lombok.version>1.16.16</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--jwt依赖包-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>${java-jwt}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jsonwebtoken.jjwt}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${alibaba.fastjson}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${apache.commons-lang3}</version>
        </dependency>

        <!--日志-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!--Apache Commons Codec-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>${apache.commons-codec}</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

注意

因为使用了@Slf4j注解,需要安装Lombok
在这里插入图片描述

结语

代码中并没有存token,请根据实际情况处理token。

最后,祝大家开开心心每一天

  • 9
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
单点登录(Single Sign-On,简称SSO)是一种身份验证技术,可以让用户只需一次登录,就可以访问多个应用程序。在实际开发中,我们可以使用Spring Boot、JWTRedis实现单点登录功能。 下面是实现单点登录的步骤: 1. 创建Spring Boot项目并引入所需依赖:spring-boot-starter-web、spring-boot-starter-data-redis和jjwt。 2. 创建一个User实体类,包含用户名和密码等信息。 3. 创建一个UserService,实现对用户信息的操作,包括注册、登录等。 4. 引入JWT依赖后,我们需要创建一个JWTUtil类,实现token的生成和解析。 5. 创建一个LoginController,用于处理用户的登录请求。在登录成功后,生成token并将其存储到Redis中。 6. 创建一个AuthController,用于验证用户的token是否有效。在验证成功后,可以获取用户信息并返回。 7. 在需要进行单点登录验证的应用程序中,只需要在请求中携带token,并调用AuthController进行验证即可。 具体实现细节可以参考以下代码示例: User实体类: ```java public class User { private String username; private String password; // 省略setter和getter方法 } ``` UserService接口: ```java public interface UserService { void register(User user); String login(String username, String password); } ``` UserService实现类: ```java @Service public class UserServiceImpl implements UserService { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public void register(User user) { // 省略用户注册逻辑 } @Override public String login(String username, String password) { // 省略用户登录逻辑 // 登录成功后生成token并存储到Redis中 String token = JWTUtil.generateToken(username); redisTemplate.opsForValue().set(username, token, 30, TimeUnit.MINUTES); return token; } } ``` JWTUtil类: ```java public class JWTUtil { private static final String SECRET_KEY = "my_secret_key"; private static final long EXPIRATION_TIME = 3600000; public static String generateToken(String username) { return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } } ``` LoginController: ```java @RestController public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody User user) { String token = userService.login(user.getUsername(), user.getPassword()); return ResponseEntity.ok(token); } } ``` AuthController: ```java @RestController public class AuthController { @Autowired private RedisTemplate<String, String> redisTemplate; @GetMapping("/auth") public ResponseEntity<User> auth(@RequestHeader("Authorization") String token) { String username = JWTUtil.getUsernameFromToken(token); if (StringUtils.isEmpty(username)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } String redisToken = redisTemplate.opsForValue().get(username); if (!token.equals(redisToken)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } User user = new User(); user.setUsername(username); return ResponseEntity.ok(user); } } ``` 在请求中携带token的示例: ```java @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> { String token = // 从Redis中获取token request.getHeaders().add("Authorization", token); return execution.execute(request, body); })); return restTemplate; } } ``` 以上就是使用Spring Boot、JWTRedis实现单点登录的步骤和示例代码。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值