权限与安全框架:Spring Security+JWT 整合(一)

依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <!--工具类依赖-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

配置文件

#cookie的名字
jwt.cookieName=mange_token
jwt.pubKeyPath=src\\main\\resources\\rsa\\rsa.pub
jwt.priKeyPath=src\\main\\resources\\rsa\\rsa.pri
jwt.secret=hfut@laboratory_11.4
#生产环境设置为180分钟
jwt.expireMinutes=18000

读取配置文件中jwt信息

在这里插入图片描述

  • JwtConfig
/**
 * 读取配置文件的jwt信息
 */
@Configuration
@ConfigurationProperties(prefix = "jwt")
@Data
public class JwtConfig {

    private String cookieName;
    private String pubKeyPath;
    private String priKeyPath;
    private String secret;
    private Integer expireMinutes;

/**
 *  会在解析私钥报错,
 *  所以要使用时还是:RsaUtils.getPrivateKey(jwtConfig.getPriKeyPath())
 *
 *   private PublicKey publicKey;
 *   private PrivateKey privateKey;
 *
 *   @PostConstruct
 *   public void createRsaKey() throws Exception {
 *     publicKey = RsaUtils.getPublicKey(pubKeyPath);
 *     privateKey = RsaUtils.getPrivateKey(pubKeyPath);
 *  } 
 */

}

实体类

在这里插入图片描述

  • 五张表(建好后用mybatis-plus逆向生成)
    • user: id、username(必须为username)、password、…其他字段
    • role: id、role_name、…其他字段
    • permession: id、permission_name、…其他字段
    • user_role: id、user_id、role_id
    • role_permession: id、role_id、permission_id

需要改变的类

  • User:需要继承UserDetails,因为Spring Security保存的对象是UserDetails的子类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    //非数据库字段 不然每次jwt的拦截器每次都要再去数据库查对应的权限 影响性能
    private List<Permission> perms;

    //数据库中其他字段
    private Integer status;

    //必须加JsonIgnore字段 不然在json序列化和反序列化的时候会带上下面这些函数的返回值
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return perms;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • Permission:继承GrantedAuthority,因为UserDetails的实现类要返回GrantedAuthority的实现类列表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission implements GrantedAuthority {

    private Integer id;
    private String permissionName;
    //描述(数据库中该类其他字段)
   private String permissionDesc;

    @JsonIgnore
    @Override
    public String getAuthority() {
        return permissionName;
    }
}
  • 其他三个实体类无需改变 直接mybatis-plus逆向生成

jwt的Token中存储的对象

在这里插入图片描述

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payload<T> {
    //token中载荷的id 在解析token的时候可以获得
    private String id;
    //这里指的前面的User实体类
    private T userInfo;
    //token过期的时间 在解析token的时候可以获得
    private Date expiration;
}

工具类

在这里插入图片描述

jwt包下的
  • JwtUtils(如果UserInfo有字段改变 需要修改这个工具类)
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.serialize(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.serialize(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.parse(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;
    }
}
  • RsaUtils
public class RsaUtils {
    /**
     * 从文件中读取公钥
     *
     * @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
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

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

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        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);
    }
}
  • JwtTokenUtils
@Slf4j
public class JwtTokenUtils {

    /**
     * 给cookie设置jwtToken
     *
     */
    public static void setJwtToken(HttpServletRequest request, HttpServletResponse response, Object userInfo) throws Exception {
        JwtConfig jwtConfig=BeanUtils.getBean(JwtConfig.class,request);
        //新生成token
        String token=JwtUtils.generateTokenExpireInMinutes(userInfo,RsaUtils.getPrivateKey(jwtConfig.getPriKeyPath()),jwtConfig.getExpireMinutes());
        CookieUtils.setCookie(request,response, jwtConfig.getCookieName(),token,null,null);
    }

    /**
     * 从request中直接读取cookie中的信息 然后重新设置到cookie中
     */
    public static void setJwtTokenByInfoFormCookie(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Object userInfo= getUserInfoFromToken(request,Object.class);
        if(userInfo==null){
            log.info(JwtTokenUtils.class.getName()+"setJwtTokenByInfoFormCookie:error");
            return;
        }
        setJwtToken(request,response,userInfo);
    }

    /**
     * 从HttpServletRequest中获PayLoad
     */
    public static<T> Payload<T> getPayLoadFromToken(HttpServletRequest request,Class<T> userInfoClass){
        JwtConfig jwtConfig = BeanUtils.getBean(JwtConfig.class, request);
        String token= CookieUtils.getCookieValue(request,jwtConfig.getCookieName());
        Payload payload=null;
        try {
            PublicKey publicKey = RsaUtils.getPublicKey(jwtConfig.getPubKeyPath());
            payload= JwtUtils.getInfoFromToken(token,publicKey,userInfoClass);
            return payload;
        } catch (Exception e) {
            log.info(JwtTokenUtils.class.getName()+"getPayLoadFromToken:error");
            return null;
        }
    }

    /**
     * 从HttpServletRequest中获取userInfo
     */
    public static<T> T getUserInfoFromToken(HttpServletRequest request,Class<T> userInfoClass){
        try {
            return getPayLoadFromToken(request,userInfoClass).getUserInfo();
        }catch (Exception e) {
            log.info(JwtTokenUtils.class.getName()+"getUserInfoFromToken:error");
            return null;
        }
    }


}

CookieUtils
/**
 *
 * Cookie 工具类
 *
 */
public final class CookieUtils {

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

    /**
     * 得到Cookie的值, 不编码
     *
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     * 得到Cookie的值,
     *
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null){
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("Cookie Decode Error.", e);
        }
        return retValue;
    }

    /**
     * 得到Cookie的值,
     *
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null){
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("Cookie Decode Error.", e);
        }
        return retValue;
    }

    /**
     * 生成cookie,并指定编码
     * @param request 请求
     * @param response 响应
     * @param cookieName name
     * @param cookieValue value
     * @param encodeString 编码
     */
    public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) {
        setCookie(request,response,cookieName,cookieValue,null,encodeString, null);
    }

    /**
     * 生成cookie,并指定生存时间
     * @param request 请求
     * @param response 响应
     * @param cookieName name
     * @param cookieValue value
     * @param cookieMaxAge 生存时间
     */
    public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) {
        setCookie(request,response,cookieName,cookieValue,cookieMaxAge,null, null);
    }

    /**
     * 设置cookie,不指定httpOnly属性
     */
    public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) {
        setCookie(request,response,cookieName,cookieValue,cookieMaxAge,encodeString, null);
    }

    /**
     * 设置Cookie的值,并使其在指定时间内生效
     *
     * @param cookieMaxAge
     *            cookie生效的最大秒数
     */
    public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) {
        try {
            if(StringUtils.isBlank(encodeString)) {
                encodeString = "utf-8";
            }

            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxAge != null && cookieMaxAge>0)
                cookie.setMaxAge(cookieMaxAge);
            if (null != request)// 设置域名的cookie
                cookie.setDomain(getDomainName(request));
            cookie.setPath("/");

            if(httpOnly != null) {
                cookie.setHttpOnly(httpOnly);
            }
            response.addCookie(cookie);
        } catch (Exception e) {
            logger.error("Cookie Encode Error.", e);
        }
    }

    /**
     * 得到cookie的域名
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;

        try{
            if(request.getMethod().toUpperCase().equals("GET")){
                domainName=request.getHeader("Referer").split("/")[2];
            }else {
                domainName=request.getHeader("Origin").split("//")[1];
            }
        }catch (NullPointerException e){
            domainName=request.getHeader("Host").split(":")[0];
            if(StringUtils.isBlank(domainName)){
                throw new RuntimeException("无法获得设置cookie的domain");
            }
        }
        return domainName;
    }
}
QueryWapperUtils
/**
 * mybatis-plus生成查询条件的工具类
 */
public class QueryWapperUtils {

    public static QueryWrapper getInWapper(String colum,Object... s){
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.in(colum,s);
        return queryWrapper;
    }

    public static QueryWrapper getLikeWapper(String colum,Object s){
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.like(colum,s);
        return queryWrapper;
    }

}
其他几个工具类
  • 在我的博客:https://blog.csdn.net/weixin_43934607/article/details/102301364
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值