springboot+security+jwt+mysql 动态权限 利用拦截器+token实现一个账号不能多人同时登陆...

项目路径: https://gitee.com/huatao1994/springbootSecurityJwtToken

一、数据库

up-799b9e29d22835a6fa0191954f77f4b985a.png

二、controller

package cn.**.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.***.security.controller
 * @Author: huat
 * @Date: 2019/12/12 14:56
 * @Version: 1.0
 */
@RestController
public class UserController {

    @RequestMapping("/index")
    public String login(){

        return "index";
    }
    @RequestMapping("test")
    public String test(){
        return "hello test";
    }


}

三、service

package cn.**.service;



import cn.**.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.**.utile
 * @Author: huat
 * @Date: 2019/12/12 14:48
 * @Version: 1.0
 */
@Service//将这个类注入到spring容器中
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return userDao.getUser(s);
    }

四、dao

package cn.**.dao;

import cn.**.entity.AuthorityUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.**.security.dao
 * @Author: huat
 * @Date: 2019/12/12 15:04
 * @Version: 1.0
 */
@Mapper
public interface UserDao {
    /**
     * 根据用户名查询角色
     * @param username 用户名
     * @return
     */
    AuthorityUser getUser(@Param("username") String username);

    /**
     * 添加用户
     * @param username 用户名
     * @param password  密码
     * @return
     */
    int saveUser(@Param("username") String username, @Param("password") String password);

    /**
     * 保存token
     * @param id
     * @param token
     * @return
     */
    int updateUserToken(@Param("id")int id,@Param("token")String token);
    /**
     * 根据用户id返回token
     * @param id
     * @return 用户token
     */
    String getTokenById(@Param("id")int id);
}

dao层mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.**.dao.UserDao">
    <resultMap id="userRole" type="authorityUser">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <collection property="authorityRoles" ofType="authorityRole">
            <id column="rid" property="rid"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleNameCN" column="role_name_CN"></result>
        </collection>
    </resultMap>
    <select id="getUser" resultMap="userRole">
        select u.id id,u.password password, u.username username,r.id rid,r.role_name,r.role_name_CN from authority_user u
        left join authority_user_role ur
        on ur.user_id=u.id
        left join authority_role r
        on ur.role_id=r.id
        where username=#{username}
    </select>
    <insert id="saveUser">
        insert into authority_user(username,password) values(#{username},#{password})
    </insert>
    <update id="updateUserToken">
        update authority_user set token=#{token} where id=#{id}
    </update>
    <select id="getTokenById" resultType="String">
        select token from authority_user where id=#{id}
    </select>
</mapper>

五、entity

AuthorityUser

package cn.**.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.**.security.entity
 * @Author: huat
 * @Date: 2019/12/12 15:12
 * @Version: 1.0
 * 创建实体类第一种方式
 */
public class AuthorityUser implements UserDetails {
    private int id;
    private String username;
    private String password;
    private List<AuthorityRole> authorityRoles;


    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public List<AuthorityRole> getAuthorityRoles() {
        return authorityRoles;
    }

    public void setAuthorityRoles(List<AuthorityRole> authorityRoles) {
        this.authorityRoles = authorityRoles;
    }

    @Override
    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorityRoles;
    }

    @Override
    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 当前用户是否可用
     * @return
     */
    @Override
    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    public boolean isEnabled() {
        return true;
    }
}

AuthorityRole

package cn.**.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;

/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.**.security.entity
 * @Author: huat
 * @Date: 2019/12/12 16:14
 * @Version: 1.0
 */
public class AuthorityRole implements GrantedAuthority {
    private int rid;
    private String roleName;
    private String roleNameCN;

    public int getRid() {
        return rid;
    }

    public void setRid(int rid) {
        this.rid = rid;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleNameCN() {
        return roleNameCN;
    }

    public void setRoleNameCN(String roleNameCN) {
        this.roleNameCN = roleNameCN;
    }

    @JsonIgnore//忽略此属性  转成json字符串时不进行转换
    @Override
    public String getAuthority() {
        return roleName;
    }
}

Payload

package cn.**.entity;

import java.util.Date;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.entity
 * @Author: huat
 * @Date: 2019/12/24 13:13
 * @Version: 1.0
 */
public class Payload<T> {
    private String id;
    private T userInfo;
    private Date 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;
    }
}

六、filter

JwtTokenFilter

package cn.**.filter;

import cn.**.dao.UserDao;
import cn.**.entity.AuthorityRole;
import cn.**.entity.AuthorityUser;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.filter
 * @Author: huat
 * @Date: 2019/12/24 17:04
 * @Version: 1.0
 */

public class JwtTokenFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authentication;
    private RsaKeyProperties rsaKeyProperties;
    private UserDao userDao;


    public JwtTokenFilter(AuthenticationManager authentication, RsaKeyProperties rsaKeyProperties,UserDao userDao) {
        this.authentication = authentication;
        this.rsaKeyProperties = rsaKeyProperties;
        this.userDao=userDao;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        try {
            //从ajax请求流读取参数,转换成AuthorityUser实体类
            AuthorityUser authorityUser= new ObjectMapper().readValue(request.getInputStream(), AuthorityUser.class);
            //参数是前台传过来的
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorityUser.getUsername(), authorityUser.getPassword());

            return authentication.authenticate(authRequest);

        } catch (IOException e) {
            e.printStackTrace();
          try {
              response.setContentType("application/json;charset=utf-8");
              //返回401状态码 权限不足
              response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
              //响应流
              PrintWriter out=response.getWriter();
              //封装返回数据
              Map<String,Object> resultMap=new HashMap<String,Object>();
              resultMap.put("code",HttpServletResponse.SC_UNAUTHORIZED);
              resultMap.put("data","");
              resultMap.put("msg","用户名密码错误");
              //new ObjectMapper().writeValueAsString(resultMap)将map转成json
              out.write(new ObjectMapper().writeValueAsString(resultMap));
              out.flush();
              out.close();
          }catch (Exception outEX){
              outEX.printStackTrace();
          }
            throw new RuntimeException(e);
        }

    }

    @Override
    public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
       AuthorityUser user=(AuthorityUser)authResult.getPrincipal();
     /*  user.setId(((AuthorityUser)authResult.getPrincipal()).getId());
       user.setUsername(authResult.getName());
       user.setAuthorityRoles((List<AuthorityRole>) authResult.getAuthorities());*/
        String token= JwtUtil.generateTokenExpireInMinutes(user,rsaKeyProperties.getPrivateKey(),24 * 60);
        response.setHeader("Authorization","bearer "+token);
        try {
            //将token保存到数据库当中
            int reslut=userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),token);
            //如果保存成功给用户返回
            if(reslut>0){
             response.setContentType("application/json;charset=utf-8");
             //返回200状态码
             response.setStatus(HttpServletResponse.SC_OK);
             //响应流
             PrintWriter out=response.getWriter();
             //封装返回数据
             Map<String,Object> resultMap=new HashMap<String,Object>();
             resultMap.put("code",HttpServletResponse.SC_OK);
             resultMap.put("data","");
             resultMap.put("msg","认证通过");
             //new ObjectMapper().writeValueAsString(resultMap)将map转成json
             out.write(new ObjectMapper().writeValueAsString(resultMap));
             out.flush();
             out.close();
         }
        }catch (Exception outEX){
            outEX.printStackTrace();
            userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),"");
        }
    }
}

JwtVerifyFileter

package cn.**.filter;

import cn.**.entity.AuthorityUser;
import cn.**.entity.Payload;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.filter
 * @Author: huat
 * @Date: 2019/12/24 18:03
 * @Version: 1.0
 */
public class JwtVerifyFileter extends BasicAuthenticationFilter {
    private RsaKeyProperties rsaKeyProperties;

    public JwtVerifyFileter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) {
        super(authenticationManager);
        this.rsaKeyProperties = rsaKeyProperties;
    }

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // request.getHeader("Authorization")这个括号中的值需要和
        // JwtTokenFilter这个类中successfulAuthentication方法中
        //  response.setHeader("Authorization","bearer "+token);的括号中的第一个值一致
        String header = request.getHeader("Authorization");
        // header.startsWith("bearer ")这个括号中的值需要和
        // JwtTokenFilter这个类中successfulAuthentication方法中
        // response.setHeader("Authorization","bearer "+token);的括号中的第二个值加号前一致
        if (header == null || !header.startsWith("bearer ")) {
            //如果没有登陆提示用户进行登陆
            response.setContentType("application/json;charset=utf-8");
            //返回403状态码
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            //响应流
            PrintWriter out = response.getWriter();
            //封装返回数据
            Map<String, Object> resultMap = new HashMap<String, Object>();
            resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
            resultMap.put("data", "");
            resultMap.put("msg", "请登录");
            //new ObjectMapper().writeValueAsString(resultMap)将map转成json
            out.write(new ObjectMapper().writeValueAsString(resultMap));
            out.flush();
            out.close();

            chain.doFilter(request, response);
        } else {
            //获取token
            String token=header.replace("bearer ","");
            //验证token是否正确
            Payload<AuthorityUser> payload=JwtUtil.getInfoFromToken(token,rsaKeyProperties.getPublicKey(), AuthorityUser.class);
            AuthorityUser user=payload.getUserInfo();
           if(null!=user){
               UsernamePasswordAuthenticationToken authResult=new UsernamePasswordAuthenticationToken(user.getUsername(),null,user.getAuthorities());
               SecurityContextHolder.getContext().setAuthentication(authResult);
               chain.doFilter(request, response);
           }
        }
    }
}

七、util

JsonUtil

package cn.**.util;

import cn.**.entity.AuthorityRole;
import cn.**.entity.AuthorityUser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.util
 * @Author: huat
 * @Date: 2019/12/24 10:58
 * @Version: 1.0
 */
@Slf4j
public class JsonUtil {
    public static final ObjectMapper objectMapper=new ObjectMapper();

    /**
     * 将对象转为json
     * @param object  对象
     * @return json
     */
    public static String toString(Object object) {
        if(null==object){
            return null;
        }
        if(object.getClass()==String.class){
            return (String)object;
        }
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.info(object+"序列化出错"+e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将json转换成对象
     * @param json json字符串
     * @param tClass 类
     * @param <T> 泛型
     * @return 返回对象
     */
    public static <T> T toBean(String json,Class<T> tClass){
        try {
            return objectMapper.readValue(json,tClass);
        } catch (IOException e) {
            log.info(json+"转对象失败"+e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 将json转换成集合对象
     * @param json json字符串
     * @param eClass 类 集合对象
     * @param <E> 泛型
     * @return 返回对象
     */
    public static <E> List<E> toList(String json,Class<E> eClass){
        try {
            return objectMapper.readValue(json,objectMapper.getTypeFactory().constructCollectionType(List.class,eClass));
        } catch (IOException e) {
            log.info(json+"转对象失败"+e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将json转换成Map对象
     * @param json json字符串
     * @param kClass 键对象
     * @param vClass 值对象
     * @param <K> 键的类型
     * @param <V> 值的类型
     * @return map对象
     */
    public static <K,V> Map<K,V> toMap(String json,Class<K> kClass,Class<V> vClass){
        try {
            return objectMapper.readValue(json,objectMapper.getTypeFactory().constructMapType(Map.class,kClass,vClass));
        } catch (IOException e) {
            log.info(json+"转对象失败"+e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 复杂类型转换  泛型套泛型
     * @param json json字符串
     * @param type  返回类型
     * @param <T>
     * @return 返回对象
     */
    public static <T> T nativeRead(String json, TypeReference<T> type){
        try {
            return objectMapper.readValue(json,type);
        } catch (IOException e) {
            log.info(json+"转对象失败"+e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
      //测试nativeRead方法
      String json="{\"id\":1,\"username\":\"张三\",\"password\":\"123\",\"authorityRoles\":[{\"rid\":0,\"roleName\":\"张三0\",\"roleNameCN\":null},{\"rid\":1,\"roleName\":\"张三1\",\"roleNameCN\":null},{\"rid\":2,\"roleName\":\"张三2\",\"roleNameCN\":null},{\"rid\":3,\"roleName\":\"张三3\",\"roleNameCN\":null},{\"rid\":4,\"roleName\":\"张三4\",\"roleNameCN\":null}]}\n";
      JsonUtil.nativeRead(json,new TypeReference<AuthorityUser>(){});

    }
}

JwtUtil

package cn.**.util;

import cn.**.entity.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;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.util
 * @Author: huat
 * @Date: 2019/12/24 12:44
 * @Version: 1.0
 */
public class JwtUtil {
    private final static String JWT_PAYLOAD_USER_KEY = "user";

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

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

    /**
     * 公钥解析token
     * @param token 用户请求的令牌
     * @param publicKey 公钥
     * @return
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey){
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }
    /**
     * 获取token中的载荷信息
     * @param token 用户请求的令牌
     * @param publicKey 公钥
     * @param userType  用户类型
     * @param <T>
     * @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> payload = new Payload<T>();
        payload.setId(body.getId());
        payload.setUserInfo(JsonUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(),userType));
        payload.setExpiration(body.getExpiration());
        return payload;
    }
    /**
     * 获取token中的载荷信息
     * @param token 用户请求的令牌
     * @param publicKey 公钥
     * @param <T>
     * @return  用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> payload = new Payload<T>();
        payload.setId(body.getId());
        payload.setExpiration(body.getExpiration());
        return payload;
    }
}

RsaKeyProperties

package cn.**.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.util
 * @Author: huat
 * @Date: 2019/12/24 15:58
 * @Version: 1.0
 */
@Configuration
@ConfigurationProperties("rsa.key")//从配置文件中获取头信息是rsa.key的配置
public class RsaKeyProperties {
    private String publicKeyFile;
    private String privateKeyFile;
    private PublicKey publicKey;
    private PrivateKey privateKey;
    @PostConstruct//在初始化完成之后执行此方法
    public void createRsaKey() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        publicKey=RsaUtil.getPublicKey(publicKeyFile);
        privateKey=RsaUtil.getPrivateKey(privateKeyFile);
    }

    public String getPublicKeyFile() {
        return publicKeyFile;
    }

    public void setPublicKeyFile(String publicKeyFile) {
        this.publicKeyFile = publicKeyFile;
    }

    public String getPrivateKeyFile() {
        return privateKeyFile;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    public void setPrivateKeyFile(String privateKeyFile) {
        this.privateKeyFile = privateKeyFile;
    }
}

RsaUtil

package cn.**.util;

import java.io.File;
import java.io.FileOutputStream;
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;

/**
 * @ProjectName: springSecuritySeparate
 * @Package: cn.**.util
 * @Author: huat
 * @Date: 2019/12/24 13:29
 * @Version: 1.0
 */
public class RsaUtil {
    private final static int DEFAULT_KEY_SIZE=2048;
    /**
     * 根据密文,生成rea公钥和私钥,并写入指定文件
     * @param publicKeyFileName 公钥文件路径
     * @param privateKeyFileName 私钥文件路径
     * @param secret 盐
     * @param keySize 大小
     */
    public static void generateKey(String publicKeyFileName,String privateKeyFileName,String secret, int keySize) throws NoSuchAlgorithmException, IOException {
        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[] publicKeyByte=keyPair.getPublic().getEncoded();
        publicKeyByte= Base64.getEncoder().encode(publicKeyByte);
        writeFile(publicKeyFileName,publicKeyByte);
        //获取公钥并写入
        byte[] privateKeyByte=keyPair.getPrivate().getEncoded();
        privateKeyByte= Base64.getEncoder().encode(privateKeyByte);
        writeFile(privateKeyFileName,privateKeyByte);
    }

    /**
     * 获取秘钥
     * @param bytes 秘钥的字节形式
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes=Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec=new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory=KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(spec);
    }
    /**
     * 获取公钥
     * @param bytes 公钥的字节形式
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes=Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec=new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory=KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(spec);
    }

    /**
     * 从文件中读取公钥
     * @param fileName 公钥保存路径 相对于classpath
     * @return 公钥对象
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PublicKey getPublicKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        byte[] bytes=readFile(fileName);
        return getPublicKey(bytes);
    }
    /**
     * 从文件中读取公钥
     * @param fileName 公钥保存路径 相对于classpath
     * @return 私钥对象
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PrivateKey getPrivateKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        byte[] bytes=readFile(fileName);
        return getPrivateKey(bytes);
    }
    private static byte[] readFile(String fileName) throws IOException {
        return Files.readAllBytes(new File(fileName).toPath());
    }
    private static void writeFile(String fileName,byte[] bytes) throws IOException {
        File file=new File(fileName);
        if(!file.exists()){
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream( fileName);
        fos.write(bytes);
        fos.close();

    }
}

SpringSercurityConfig

package cn.**.util;


import cn.**.filter.JwtTokenFilter;
import cn.**.filter.JwtVerifyFileter;
import cn.**.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;


/**
 * @ProjectName: springbootSecurity
 * @Package: cn.cn.**.security.util
 * @Author: huat
 * @Date: 2019/12/14 8:06
 * @Version: 1.0
 */

/**
 * 开启security注解支持
 *
 * @EnableWebSecurity EnableGlobalMethodSecurity  方法级授权
 * (securedEnabled=true) 开启@Secured 注解过滤权限
 * (jsr250Enabled=true)开启@RolesAllowed 注解过滤权限
 * (prePostEnabled=true) 使用表达式时间方法级别的安全性         4个注解可用
 * @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)
 */
@Configuration
@EnableWebSecurity
public class SpringSercurityConfig extends WebSecurityConfigurerAdapter {
    /* @Autowired
     UserService1 userService;*/
    @Autowired
    UserService userService;
    @Autowired
    private RsaKeyProperties rsaKeyProperties;


    /**
     * 将security中加密方式注入到spring容器中
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 解决跨域
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();
        //指定允许跨域的请求(*所有):http://wap.ivt.guansichou.com
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
        // setAllowCredentials(true) is important, otherwise:
        // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
        configuration.setAllowCredentials(true);
        // setAllowedHeaders is important! Without it, OPTIONS preflight request
        // will fail with 403 Invalid CORS request
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "X-User-Agent", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    /**
     * 将账号密码设置在内存当中
     *
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                //将UserDetailsService放到容器中
                .userDetailsService(userService)
                //加密方式放入
                .passwordEncoder(passwordEncoder());
    }

    //前后端分离时候使用
    @Override
    public void configure(HttpSecurity http) throws Exception {
        //释放静态资源,指定资源拦截规则,
        // 指定自定义认证页面,指定退出认证配置,csrf(跨域伪造请求)配置
            http
                .csrf()
                .disable()//关闭csrf(跨域伪造请求)
                .cors()
                .and().servletApi().disable()
                .requestCache().disable()
                .authorizeRequests()
                .antMatchers("/index").hasAnyRole("ADMIN")
                .antMatchers("/test").hasRole("TEST")
                 .anyRequest().authenticated()//其他资源需要认证
                .and()
                .addFilter(new JwtTokenFilter(super.authenticationManager(),rsaKeyProperties))
                .addFilter(new JwtVerifyFileter(super.authenticationManager(),rsaKeyProperties))
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//禁用session
                ;

    }
}

八、启动类

package cn.**;

import cn.**.util.RsaKeyProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

/**
 * @ProjectName: springSecurity
 * @Package: cn.cn.**
 * @Author: huat
 * @Date: 2019/12/21 8:38
 * @Version: 1.0
 */
@SpringBootApplication
@EnableConfigurationProperties(RsaKeyProperties.class)//加载RsaKeyProperties类,以bean形式注入到spring容器中
public class SpringBootApplicationStart {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootApplicationStart.class,args);
    }
}

九、配置文件

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8
mybatis:
  type-aliases-package: cn.**.entity
logging:
  level:
    cn.**.dao: debug
rsa:
  key:
    #公钥保存地方
    publicKeyFile: E:\test\public.pub
    #私钥保存地方
    privateKeyFile: E:\test\private

十、拦截器

package cn.**.filter;

import cn.**.dao.UserDao;
import cn.**.entity.AuthorityUser;
import cn.**.entity.Payload;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @ProjectName: springbootSecurityJwtOAuth
 * @Package: cn.**.filter
 * @Author: huat
 * @Date: 2019/12/26 13:18
 * @Version: 1.0
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private UserDao userDao;
    @Autowired
    private RsaKeyProperties rsaKeyProperties;
    //这个方法是在访问接口之前执行的,我们只需要在这里写验证登陆状态的业务逻辑,就可以在用户调用指定接口之前验证登陆状态了
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头中的信息
        String header=request.getHeader("Authorization");
        if (null!=header&&header.startsWith("bearer ")){
            //获取token
            String requestToken=header.replace("bearer ","");
            //验证token是否正确
            Payload<AuthorityUser> payload= JwtUtil.getInfoFromToken(requestToken,rsaKeyProperties.getPublicKey(), AuthorityUser.class);
            AuthorityUser user=payload.getUserInfo();
            String token=userDao.getTokenById(user.getId());
            if(null!=token&&!"".equals(token)){
                if( token.equals(requestToken)){
                    return true;
                }
                //如果没有登陆提示用户进行登陆
                response.setContentType("application/json;charset=utf-8");
                //返回403状态码
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                //响应流
                PrintWriter out = response.getWriter();
                //封装返回数据
                Map<String, Object> resultMap = new HashMap<String, Object>();
                resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
                resultMap.put("data", "您的账号在其他地方已登录");
                resultMap.put("msg", "请登录");
                //new ObjectMapper().writeValueAsString(resultMap)将map转成json
                out.write(new ObjectMapper().writeValueAsString(resultMap));
                out.flush();
                out.close();
                return false;
            }
        }
        //如果没有登陆提示用户进行登陆
        response.setContentType("application/json;charset=utf-8");
        //返回403状态码
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        //响应流
        PrintWriter out = response.getWriter();
        //封装返回数据
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
        resultMap.put("data", "");
        resultMap.put("msg", "请登录");
        //new ObjectMapper().writeValueAsString(resultMap)将map转成json
        out.write(new ObjectMapper().writeValueAsString(resultMap));
        out.flush();
        out.close();

        return false;
    }



}

package cn.**.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @Author: huat
 * @Date: 2019/7/26 10:18
 * @Version: 1.0
 */


@Configuration
public class WebConfigurer implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        //super.addResourceHandlers(registry);
    }

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns("/**") 表示拦截所有的请求,
        //excludePathPatterns("/login", "/register"); 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/loginInto", "/login","/login/**","/css/**","/font/**","/img/**","/images/**","/js/**","/lay/**");
        //super.addInterceptors(registry);    //较新Spring Boot的版本中这里可以直接去掉,否则会报错

    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是SpringBoot+Security+JWT动态授权的实现步骤: 1. 首先,我们需要添加Spring SecurityJWT的依赖。在pom.xml文件中添加如下代码: ``` <!-- Spring Security --> <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.1</version> </dependency> ``` 2. 添加JWT的配置类。创建一个JwtConfig类,用于生成JWT Token和解析JWT Token。代码如下: ``` @Configuration public class JwtConfig { private String secret = "jwt_secret_key"; private long expire = 604800000; @Bean public JwtAuthTokenGenerator jwtAuthTokenGenerator() { return new JwtAuthTokenGenerator(secret, expire); } @Bean public JwtAuthTokenVerifier jwtAuthTokenVerifier() { return new JwtAuthTokenVerifier(secret); } } ``` 3. 添加Spring Security的配置类。创建一个SecurityConfig类,配置Spring Security的一些基本设置。代码如下: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(jwtAuthenticationProvider()); } @Bean public JwtAuthenticationProvider jwtAuthenticationProvider() { return new JwtAuthenticationProvider(jwtAuthTokenVerifier()); } } ``` 4. 添加JwtAuthenticationFilter类。创建一个JwtAuthenticationFilter类,用于在每个请求中验证JWT Token。代码如下: ``` public class JwtAuthenticationFilter extends OncePerRequestFilter { private AuthenticationManager authenticationManager; public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = getToken(request); if (token != null) { JwtAuthToken authToken = new JwtAuthToken(token); Authentication authentication = authenticationManager.authenticate(authToken); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String getToken(HttpServletRequest request) { String header = request.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { return header.substring(7); } return null; } } ``` 5. 添加JwtAuthenticationProvider类。创建一个JwtAuthenticationProvider类,用于验证JWT Token是否有效。代码如下: ``` public class JwtAuthenticationProvider implements AuthenticationProvider { private JwtAuthTokenVerifier jwtAuthTokenVerifier; public JwtAuthenticationProvider(JwtAuthTokenVerifier jwtAuthTokenVerifier) { this.jwtAuthTokenVerifier = jwtAuthTokenVerifier; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { JwtAuthToken authToken = (JwtAuthToken)authentication; String token = authToken.getToken(); if (jwtAuthTokenVerifier.verify(token)) { return new JwtAuthenticatedUser(token); } throw new BadCredentialsException("Invalid JWT token"); } @Override public boolean supports(Class<?> authentication) { return JwtAuthToken.class.isAssignableFrom(authentication); } } ``` 6. 添加JwtAuthToken类。创建一个JwtAuthToken类,用于包装JWT Token。代码如下: ``` public class JwtAuthToken extends AbstractAuthenticationToken { private String token; public JwtAuthToken(String token) { super(null); this.token = token; } @Override public Object getCredentials() { return token; } @Override public Object getPrincipal() { return null; } } ``` 7. 添加JwtAuthenticatedUser类。创建一个JwtAuthenticatedUser类,用于标识已通过验证的用户。代码如下: ``` public class JwtAuthenticatedUser implements Authentication { private String token; public JwtAuthenticatedUser(String token) { this.token = token; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Collections.emptyList(); } @Override public Object getCredentials() { return null; } @Override public Object getDetails() { return null; } @Override public Object getPrincipal() { return token; } @Override public boolean isAuthenticated() { return true; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { throw new UnsupportedOperationException(); } @Override public String getName() { return null; } } ``` 8. 最后,添加一个LoginController类,用于登录并获取JWT Token。代码如下: ``` @RestController public class LoginController { @Autowired private JwtAuthTokenGenerator jwtAuthTokenGenerator; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody User user) { if (user.getUsername().equals("admin") && user.getPassword().equals("admin")) { String token = jwtAuthTokenGenerator.generateToken(user.getUsername()); return ResponseEntity.ok(token); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } } ``` 这就是SpringBoot+Security+JWT动态授权的实现步骤,希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夜燃烧的香烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值