Spring Boot + security + jwt 测试安全策略

一、测试概述

  主要目的是测试security的用法。
  因测试搭建mysql和redis比较麻烦,所以我这里将自定义的jwt和用户信息缓存到程序的内存中。
本人测试的项目比较混乱,Spring Boot父类只标出有用的依赖。其子类用的版本为jdk11。


后续会继续深入oauth2,敬请期待。
代码地址:https://gitcode.net/qq_40539437/cloud.git
如果想使用自定义jwt工具类往redis里存储请查看源码cloud-jwt模块。

整体代码结构:
在这里插入图片描述

二、maven 相关依赖

1、父类maven相关依赖

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <spring.boot.version>2.2.5.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR3</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version> <!-- 版本丛2.1.0.RELEASE升到2.2.1.RELEASE 解决nacos 域名访问问题 -->
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>
 <dependencyManagement>
        <dependencies>
            <!--springboot 2.2.5-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--Spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--Spring cloud alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、子类相关依赖

 <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>10</source>
                    <target>10</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>com.atguigu.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

二、代码块

1、配置文件 application.yaml

server:
  port: 20000

2、启动类

package com.atguigu.cloud;

import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.utils.RandomStr;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import java.util.Random;

@SpringBootApplication
@EnableWebSecurity
public class CsSecurityTestApplication {


    public static void main(String[] args) throws Exception {
        SpringApplication.run(CsSecurityTestApplication.class, args);
        key();
    }

    public static void key() throws Exception {
        Random random = new Random();
        int i = random.nextInt(60);
        String randomString = RandomStr.getRandomString(i);
        System.err.println("公 私钥随机串: " + randomString);

        // 生成密钥对
        RsaLocaleUtils.generateKey(MyLocaleCache.publicKey_S, MyLocaleCache.privateKey_S, randomString, 2048);
    }

}

3、本地缓存

package com.atguigu.cloud.cache;

import com.atguigu.cloud.domain.UserInfo;

import java.util.HashMap;
import java.util.Map;

public class MyLocaleCache {

    public static String privateKey_S = "privateKey";
    public static String publicKey_S = "publicKey";
    private static Map<Long, UserInfo> cache = new HashMap<>();

    public static UserInfo get(Long key){
        return cache.get(key);
    }

    public static UserInfo set(Long key, UserInfo userInfo){
        return cache.put(key,userInfo);
    }
}

4.配置类

package com.atguigu.cloud.conf;

import com.atguigu.cloud.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 密码加密方式 相同密码每次加密方式不同,但是每次都能与之匹配
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /** 配置Spring Security 的拦截规则*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login1").anonymous()
                .antMatchers("/test/**").hasRole("ADMIN") //需要具有 "ADMIN" 角色的用户访问 "/test/**"
                .anyRequest().authenticated(); // 其他路径需要认证


        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /** 权限管理器*/
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * {noop} 指代的是当前密码以明文输入  此处测试是指使用security自带的验证页面,设置密码,当实现UserDetailsService之后就不需要进行配置了
     * @param auth
     * @throws Exception

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("manager").password("{noop}manager").roles("manager")
                .and()
                .withUser("admin").password("{noop}admin").roles("manager", "ADMIN");
    }
     */
}

5.登录及权限验证接口

登录接口 LoginController:
这里不能直接使用login直接当接口,因为会找security里的login接口

package com.atguigu.cloud.controller;

import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.domain.LoginUser;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/login1")
public class LoginController {

    @Autowired
    private AuthenticationManager authenticationManager;


    @GetMapping()
    public Map<String, Object> login(String username, String password) throws Exception {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username , password);
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        if(authentication == null) {
            throw new RuntimeException("用户名或密码错误");
        }
        LoginUser loginUser = (LoginUser)authentication.getPrincipal();
        UserInfo userInfo = loginUser.getUser();
        MyLocaleCache.set(userInfo.getId(),userInfo);
        Map<String , Long> params = new HashMap<>() ;
        params.put("userId" , userInfo.getId()) ;
        PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(MyLocaleCache.privateKey_S);
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 5);

        // 构建返回数据
        Map<String , Object> result = new HashMap<>();
        result.put("token" , token) ;

        System.out.println(username + "=" + password);
        return result;
    }
}

测试接口TestController:

package com.atguigu.cloud.controller;


import com.atguigu.cloud.domain.UserInfo;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    @ResponseBody
    public Map<String,Object> hello(){
        UserInfo userInfo = (UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Map<String, Object> map = new HashMap<>();
        map.put("测试:","hello");
        map.put("用户:",userInfo.toString());
        return map;
    }
}

6.实体类

从写UserDetails:

package com.atguigu.cloud.domain;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/***
 * 下方其他认证可根据业务情况重写
 */
public class LoginUser implements UserDetails {
 
    private UserInfo userInfo;
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List list = new ArrayList();
        list.add(new SimpleGrantedAuthority("ROLE_"+ userInfo.getRole()));
        return list;
    }
 
    @Override
    public String getPassword() {
        return userInfo.getPassword();
    }
 
    @Override
    public String getUsername() {
        return userInfo.getUsername();
    }
 
    @Override
    public boolean isAccountNonExpired() {          // 账号是否没有过期
        return true;
    }
 
    @Override
    public boolean isAccountNonLocked() {           // 账号是否没有被锁定
        return true;
    }
 
    @Override
    public boolean isCredentialsNonExpired() {      // 账号的凭证是否没有过期
        return true;
    }
 
    @Override
    public boolean isEnabled() {                    // 账号是否可用
        return true;
    }

    public LoginUser() {
    }

    public LoginUser(UserInfo user) {
        this.userInfo = user;
    }

    public UserInfo getUser() {
        return userInfo;
    }

    public void setUser(UserInfo user) {
        this.userInfo = user;
    }
}

载荷Payload:

package com.atguigu.cloud.domain;

import java.util.Date;

public class Payload<T> {
    private String id;
    private T userInfo;
    private Date expiration;

    public Payload() {
    }

    public Payload(String id, T userInfo, Date expiration) {
        this.id = id;
        this.userInfo = userInfo;
        this.expiration = expiration;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public T getUserInfo() {
        return userInfo;
    }

    public void setUserInfo(T userInfo) {
        this.userInfo = userInfo;
    }

    public Date getExpiration() {
        return expiration;
    }

    public void setExpiration(Date expiration) {
        this.expiration = expiration;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Payload{");
        sb.append("id='").append(id).append('\'');
        sb.append(", userInfo=").append(userInfo);
        sb.append(", expiration=").append(expiration);
        sb.append('}');
        return sb.toString();
    }
}

用户信息UserInfo:

package com.atguigu.cloud.domain;

public class UserInfo {

    private Long id;

    private String username;

    private String password;

    /** 这里多个可以采用分割符分割后续可以尽心解析*/
    private String role;

    public UserInfo() {
    }

    public UserInfo(Long id, String username, String role) {
        this.id = id;
        this.username = username;
        this.role = role;
    }

    public UserInfo(Long id, String username, String password, String role) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.role = role;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("UserInfo{");
        sb.append("id=").append(id);
        sb.append(", username='").append(username).append('\'');
        sb.append(", password='").append(password).append('\'');
        sb.append(", role='").append(role).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

7.JWT过滤器

package com.atguigu.cloud.filter;

import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.domain.Payload;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 1、从请求头中获取token,如果请求头中不存在token,直接放行即可!由Spring Security的过滤器进行校验!
        String token = request.getHeader("token");
        if(token == null || "".equals(token)) {
            filterChain.doFilter(request , response);
            return ;
        }

        // 2、对token进行解析,取出其中的userId
        Payload<UserInfo> info = null;
        try {
            info = JwtUtils.getInfoFromToken(token, RsaLocaleUtils.getPublicKey("publicKey"), UserInfo.class);

        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法") ;
        }

        // 3、使用userId从redis中查询对应的LoginUser对象
        UserInfo userInfo1 = MyLocaleCache.get(info.getUserInfo().getId());
        if(userInfo1 != null) {
            List list = new ArrayList<>();
            list.add(new SimpleGrantedAuthority("ROLE_"+ userInfo1.getRole()));
            // 4、然后将查询到的LoginUser对象的相关信息封装到UsernamePasswordAuthenticationToken对象中,然后将该对象存储到Security的上下文对象中
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userInfo1, null ,list) ;
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }

        // 5、放行
        filterChain.doFilter(request , response);
    }
}

8.业务类

package com.atguigu.cloud.service;

import com.atguigu.cloud.domain.LoginUser;
import com.atguigu.cloud.domain.UserInfo;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    static Map<String, UserInfo> dataBase = new HashMap<>();

    static {
        /** 测试管理员用户*/
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1L);
        userInfo.setUsername("admin");
        userInfo.setPassword(new BCryptPasswordEncoder().encode("admin")); //不加密方式 "{noop}admin"
        userInfo.setRole("ADMIN");

        /** 测试普通用户*/
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setId(2L);
        userInfo2.setUsername("manager");
        userInfo2.setPassword(new BCryptPasswordEncoder().encode("manager")); //不加密方式 "{noop}admin"
        userInfo2.setRole("MANAGER");


        dataBase.put("admin",userInfo);
        dataBase.put("manager",userInfo2);
    }
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 
        // 根据用户名查询用户数据
        UserInfo userInfo = dataBase.get(username);
        System.out.println(userInfo);
        // 如果查询不到数据,说明用户名或者密码错误,直接抛出异常
        if(userInfo == null) {
            throw new RuntimeException("用户名或者密码错误") ;
        }
 
        // 将查询到的对象转换成Spring Security所需要的UserDetails对象  这里不需要比对密码
        return new LoginUser(userInfo);
    }
}

9.工具类

JsonUtils

package com.atguigu.cloud.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class JsonUtils {

    public static final ObjectMapper mapper = new ObjectMapper();

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

    public static String toString(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj.getClass() == String.class) {
            return (String) obj;
        }
        try {
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            logger.error("json序列化出错:" + obj, e);
            return null;
        }
    }

    public static <T> T toBean(String json, Class<T> tClass) {
        try {
            return mapper.readValue(json, tClass);
        } catch (IOException e) {
            logger.error("json解析出错:" + json, e);
            return null;
        }
    }

    public static <E> List<E> toList(String json, Class<E> eClass) {
        try {
            return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));
        } catch (IOException e) {
            logger.error("json解析出错:" + json, e);
            return null;
        }
    }

    public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) {
        try {
            return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));
        } catch (IOException e) {
            logger.error("json解析出错:" + json, e);
            return null;
        }
    }

    public static <T> T nativeRead(String json, TypeReference<T> type) {
        try {
            return mapper.readValue(json, type);
        } catch (IOException e) {
            logger.error("json解析出错:" + json, e);
            return null;
        }
    }
}

JwtUtils

package com.atguigu.cloud.utils;

import com.atguigu.cloud.domain.Payload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.UUID;


public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位分钟
     * @return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusMinutes(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位秒
     * @return JWT
     */
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return Jws<Claims>
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    private static String createJTI() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 获取token中的载荷信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setExpiration(body.getExpiration());
        return claims;
    }


    public static void main(String[] args) {

    }
}

随机加密工具类:

package com.atguigu.cloud.utils;

import java.util.Random;

public class RandomStr {
    //length用户要求产生字符串的长度
    public static String getRandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }
}

RsaLocaleUtils

package com.atguigu.cloud.utils;

import org.springframework.stereotype.Component;

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class RsaLocaleUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;

    private static ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>();

    public static void setCache(String key,String value){
        cache.put(key, value);
    }

    public static String getCache(String key){
        return cache.get(key);
    }


    /**
     *  @Author: CS
     *  @Description: 从本地缓存中获取  可以修改为redis
     */
    public static void generateKey(String publicKey, String privateKey, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        setCache(publicKey,Base64.getEncoder().encodeToString(publicKeyBytes));
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        setCache(privateKey,Base64.getEncoder().encodeToString(privateKeyBytes));
    }


    /**
     * 从Redis中 读取密钥
     *
     * @param privateKey 私钥保存的Key
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        privateKey = getCache(privateKey).toString();
        byte[] bytes  = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 从文件中 获取公钥
     *
     * @param publicKey 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        publicKey = getCache(publicKey).toString();
        byte[] bytes = Base64.getDecoder().decode(publicKey);
        System.out.println(bytes.length);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }
}

RsaUtils

package com.atguigu.cloud.utils;

import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

//import io.jsonwebtoken.security.Keys;

public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 从文件中 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    private static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }

    public static void testfor() {
        KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
        PrivateKey aPrivate = keyPair.getPrivate();
        PublicKey aPublic = keyPair.getPublic();
    }
}

10.测试类

package com.cloud.atguigu.jwt;

import com.atguigu.cloud.domain.Payload;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RandomStr;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.junit.Test;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Random;


public class JwtTest {

    private  String privateKey_S = "privateKey";
    private String publicKey_S = "publicKey";
    @Test
    public void testRSAByLocale() throws Exception {

        Random random = new Random();
        int i = random.nextInt(60);
        String randomString = RandomStr.getRandomString(i);
        randomString = "cs";
        System.err.println(randomString);

        // 生成密钥对
        RsaLocaleUtils.generateKey(publicKey_S, privateKey_S, randomString, 2048);
        // 获取私钥
        PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(privateKey_S);
        System.out.println("privateKey = " + privateKey.toString());
        PublicKey publicKey = RsaLocaleUtils.getPublicKey(publicKey_S);
        System.out.println("publicKey = " + publicKey.toString());
    }


    @Test
    public void testJWTByLocale() throws Exception {

        Random random = new Random();
        int i = random.nextInt(60);
        String randomString = RandomStr.getRandomString(i);
        randomString = "cs";
        System.err.println(randomString);

        // 生成密钥对
        RsaLocaleUtils.generateKey(publicKey_S, privateKey_S, randomString, 2048);
        // 获取私钥
        PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(privateKey_S);
        // 生成token
        String token = JwtUtils.generateTokenExpireInMinutes(new UserInfo(1L, "Jack", "guest"), privateKey, 5);


        System.out.println("token = " + token);


        System.err.println("privateKey:" + privateKey.toString());


        // 获取公钥
        PublicKey publicKey = RsaLocaleUtils.getPublicKey(publicKey_S);
        // 解析token
        Payload<UserInfo> info = JwtUtils.getInfoFromToken(token, publicKey, UserInfo.class);

        System.out.println("info.getExpiration() = " + info.getExpiration());
        System.out.println("info.getUserInfo() = " + info.getUserInfo());
        System.out.println("info.getId() = " + info.getId());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/357708.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【web安全】文件上传漏洞

upload-labs靶场 第一关 绕过前端 先打开哥斯拉&#xff0c;生成木马&#xff0c;选择php 打开brup开浏览器&#xff0c;上传文件&#xff0c;就会发现被阻止了&#xff0c;还没抓到包呢 那就是被前端代码阻止了&#xff0c;那通常前端代码都只能防御后缀名 我们抓到包后直…

分布式因果推断在美团履约平台的探索与实践

美团履约平台技术部在因果推断领域持续的探索和实践中&#xff0c;自研了一系列分布式的工具。本文重点介绍了分布式因果树算法的实现&#xff0c;并系统地阐述如何设计实现一种分布式因果树算法&#xff0c;以及因果效应评估方面qini_curve/qini_score的不足与应对技巧。希望能…

Android MediaCodec 简明教程(四):使用 MediaCodec 将视频解码到 Surface,并使用 SurfaceView 播放视频

系列文章目录 Android MediaCodec 简明教程&#xff08;一&#xff09;&#xff1a;使用 MediaCodecList 查询 Codec 信息&#xff0c;并创建 MediaCodec 编解码器Android MediaCodec 简明教程&#xff08;二&#xff09;&#xff1a;使用 MediaCodecInfo.CodecCapabilities 查…

微信小程序(二十五)条件判断语句与结构隐藏

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.条件判断语句的演示 2.隐藏结构的演示 源码&#xff1a; index.wxml <view><!-- wx:if和wx:else为条件判断语句 --><text wx:if"{{isLogin}}">已登入的用户</text><tex…

Flutter 应用服务:主题、暗黑、国际化、本地化-app_service库

Flutter应用服务 主题、暗黑、国际化、本地化-app_service库 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/det…

(一)PySpark3:安装教程及RDD编程(非常详细)

目录 一、pyspark介绍 二、PySpark安装 三、RDD编程 1、创建RDD 2、常用Action操作 ①collect ②take ③takeSample ④first ⑤count ⑥reduce ⑦foreach ⑧countByKey ⑨saveAsTextFile 3、常用Transformation操作 ①map ②filter ③flatMap ④sample ⑤d…

SQL报错注入

君衍. 一、sqllabs第五关报错注入updatexml报错注入原理及思路 二、常见的报错函数三、floor报错注入原理1、概念2、函数回顾2.1 rand函数2.2 floor(rand(0)*2)函数2.3 group by函数2.4 count(*)函数2.5 函数综合报错 3、报错分析4、总结 一、sqllabs第五关报错注入 之前我在这…

Linux系列之查看cpu、内存、磁盘使用情况

查看磁盘空间 df命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为KB。可以利用该命令来获取硬盘被占用了多少空间&#xff0c;目前还剩下多少空间等信息。使用df -h命令&#xff0c;加个-h参数是为了显示GB MB KB单位&#xff0c;这样更容易查看 Filesystem …

使用Hugging Face下载ImageNet

1. 创建Hugging Face账号&#xff0c;在个人中心的setting部分&#xff0c;生成Access Token 2. 使用在python命令行中运行&#xff1a; pip install datasets 此时需要输入token 3. 新建一个python文件 from datasets import load_dataset dset load_dataset(imagenet-1k…

http-server开启一个服务器

前言 在写前端页面中&#xff0c;经常会在浏览器运行HTML页面&#xff0c;从本地文件夹中直接打开的一般都是file协议&#xff0c;当代码中存在http或https的链接时&#xff0c;HTML页面就无法正常打开&#xff0c;为了解决这种情况&#xff0c;需要在在本地开启一个本地的服务…

vuex store,mutations,getters,actions

文章目录 1.vuex概述2.构建vuex【多组件数据共享】环境Son1.vueSon2.vueApp.vue 3.创建一个空仓库4.如何提供&访问vuex的数据①核心概念 - state状态1.通过store直接访问2.通过辅助函数简化代码 ②核心概念 - mutations&#xff08;粗略&#xff09; 5.核心概念 - mutation…

如何在 VM 虚拟机中安装 Kail Linux 2023.4 操作系统保姆级教程(附链接)

一、VMware Workstation 虚拟机 先得安装 VM 虚拟机&#xff0c;没有的可以参考这篇文章安装 VM 虚拟机 如何在 VM 虚拟机中安装 Win10 操作系统保姆级教程&#xff08;附链接&#xff09;https://eclecticism.blog.csdn.net/article/details/135713915 二、Kail 镜像 进入…

树控件、下拉框、文本框常用测试用例

01 控件的测试外观操作 1&#xff09;项目中的所有树是否风格一致 2&#xff09;树结构的默认状态是怎样的。比如默认树是否是展开&#xff0c;是展开几级&#xff1f; 是否有默认的焦点&#xff1f;默认值是什么&#xff1f;展开的节点图标和颜色&#xff1f; 3&#xff09…

关于如何将Win幻兽帕鲁服务端存档转化为单人本地存档的一种方法(无损转移)

本文转自博主的个人博客&#xff1a;https://blog.zhumengmeng.work,欢迎大家前往查看。 原文链接&#xff1a;点我访问 **起因&#xff1a;**最近大火的开放世界缝合体游戏幻兽帕鲁的大火也是引起了博主的注意&#xff0c;然后博主和周边小伙伴纷纷入手&#xff0c;博主也是利…

谷歌人工智能视频生成器-LUMIERE(未开源)

Google重磅发布视频生成模型Lumiere 据说后续会开源 亮点1.支持文本到视频与图像到视频 亮点2.画风迁移 亮点3.运动蒙版 亮点4.视频编辑 亮点5.视频修复 谷歌视频模型可以生成80帧的片段&#xff01;不仅画质好、质量高&#xff0c;而且时长更长。 视频局部编辑 这项功能可以…

07.领域驱动设计:了解3种常见微服务架构模型的对比和分析

目录 1、概述 2、整洁架构 3、六边形架构 4、三种微服务架构模型的对比和分析 5、从三种架构模型看中台和微服务设计 5.1 中台建设要聚焦领域模型 5.2 微服务要有合理的架构分层 5.2.1 项目级微服务 5.2.2 企业级中台微服务 5.3 应用和资源的解耦与适配 6、总结 1、概…

C语言-动态内存申请

一、动态分配内存的概述 在数组一章中&#xff0c;介绍过数组的长度是预先定义好的&#xff0c;在整个程序中固定不变&#xff0c;但是在实际的编程中&#xff0c;往往会发生这种情况&#xff0c;即所需的内存空间取决于实际输入的数据&#xff0c;而无法预先确定 。为了解决…

一、创建Vue3项目

1. 下载 node.js 下载地址&#xff1a;https://nodejs.org/zh-cn 优先选择 16 版本; node -v || node -version 可以检查本地 node.js 版本 2. 设置淘宝镜像源 npm config set registry https://registry.npmmirror.com/ 设置淘宝镜像源 npm config get registry 查看当前镜像…

常见的网络安全威胁和防护方法

随着数字化转型和新兴技术在各行业广泛应用&#xff0c;网络安全威胁对现代企业的业务运营和生产活动也产生了日益深远的影响。常见的网络安全威胁通常有以下几种&#xff1a; 1. 钓鱼攻击 攻击者伪装成合法的实体&#xff08;如银行、电子邮件提供商、社交媒体平台等&#xf…

解析PDF二维码:数字时代文件管理的创新之道

随着数字时代的来临&#xff0c;文件管理方式正经历着翻天覆地的变革。在这个变革的浪潮中&#xff0c;PDF二维码作为一种创新的技术手段&#xff0c;正逐渐引起人们的关注。本文将深入探讨PDF二维码的概念、应用领域以及在文件管理中的前景。 一、PDF二维码的概念 PDF二维码…