springboot基于aop redis token实现token验证和权限验证

springboot基于aop redis token实现token验证和权限验证_第1张图片

 开始之前了解: ras 公钥密钥 解析加密 token,aop实现自定义注解,登录用到一点mybatis plus,这篇文章只有后端代码

源码:https://gitee.com/sun-jianhui/leyou(在dev-sjh分支下)

用到的数据库(数据库有点绕)

 

 管理员表

 角色表

权限表

springboot基于aop redis token实现token验证和权限验证_第2张图片 角色-权限 关联表

 springboot基于aop redis token实现token验证和权限验证_第3张图片

token生成,解析,加密

JwtUtils

public class JwtUtils {
    /**
     *  私钥加密token
     * @param data 需要加密的数据(载荷内容)
     * @param expireMinutes 过期时间,单位:分钟
     * @param privateKey 私钥
     * @return
     */
    public static String generateToken(Object data, int expireMinutes, PrivateKey         privateKey) throws Exception {
        //1 获得jwt构建对象
        JwtBuilder jwtBuilder = Jwts.builder();
        //2 设置数据
        if( data == null ) {
            throw new RuntimeException("数据不能为空");
        }
        BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 获得属性值
            Object value = propertyDescriptor.getReadMethod().invoke(data);
            if(value != null) {
                jwtBuilder.claim(name,value);
            }
        }
        //3 设置过期时间
        jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
        //4 设置加密
        jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
        //5 构建
        return jwtBuilder.compact();
    }

    /**
     * 通过公钥解析token
     * @param token 需要解析的数据
     * @param publicKey 公钥
     * @param beanClass 封装的JavaBean
     * @return
     * @throws Exception
     */
    public static  T  getObjectFromToken(String token, PublicKey publicKey, Class beanClass) throws Exception {
        //1 获得解析后内容
        Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
        //2 将内容封装到对象JavaBean
        T bean = beanClass.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 通过属性名,获得对应解析的数据
            Object value = body.get(name);
            if(value != null) {
                // 将获得的数据封装到对应的JavaBean中
                BeanUtils.setProperty(bean,name,value);
            }
        }

        return bean;
    }

}

RasUtils

public class RasUtils {
    public static String publicKeyPath="E:\\project\\IdeaProjects\\jwt_demo\\test\\ras.pub";
    public static String privateKeyPath="E:\\project\\IdeaProjects\\jwt_demo\\test\\ras.pri";

    /**
     * 从文件中读取公钥
     *
     * @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 Exception
     */
    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.getParentFile().exists()){
            dest.getParentFile().mkdirs();
        }
        //创建需要的文件
        if (!dest.exists()) {
            dest.createNewFile();
        }

        Files.write(dest.toPath(), bytes);
    }
}

 登录

@Service
public class AdministratorsManagerServiceImpl extends ServiceImpl implements AdministratorsManagerService {
    @Autowired
    private AdministratorsManagerMapper administratorsManagerMapper;
    @Autowired
    private AdministratorsAuthorityMapper authorityMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public ResultLogin managerLogin(ManagerLoginVo managerLoginVo) {
        ResultLogin result=new ResultLogin();
        String token=null;

        QueryWrapper wrapper=new QueryWrapper();
        wrapper.eq("manager_account_number",managerLoginVo.getManagerAccountNumber());
        wrapper.eq("manager_password",managerLoginVo.getManagerPassword());

        AdministratorsManager administratorsManager = administratorsManagerMapper.selectOne(wrapper);
        //登录成功
        if(administratorsManager!=null) {
            List listByManagerNumber = authorityMapper.getListByManagerNumber(administratorsManager.getRoleNumber());

            //将管理员编号 放入session 并将session为键权限编号为值的数据存入redis。方便权限认证
            String managerAccountNumber = administratorsManager.getManagerAccountNumber();
            SetOperations zSetOperations = redisTemplate.opsForSet();
            zSetOperations.members(managerAccountNumber);
            for(String number:listByManagerNumber){
                zSetOperations.add(managerAccountNumber,number);
            }

            //将userLogin 加密生成token传递给前端
            result.setCode("200");
            try {
                UserLogin userLogin=new UserLogin();
                userLogin.setUserName(administratorsManager.getManagerName());
                userLogin.setManagerAccountNumber(administratorsManager.getManagerAccountNumber());
                userLogin.setRoleNumber(administratorsManager.getRoleNumber());
                 token = JwtUtils.generateToken(userLogin, 60*24*7, RasUtils.getPrivateKey(RasUtils.privateKeyPath));
            } catch (Exception e) {
                e.printStackTrace();
            }
            result.setResult(Result.RESULT_FLG.SUCCESS.getValue());
            result.setData(administratorsManager);
            result.setMsg("登录成功");
            result.setToken(token);
        }else {
            result.setCode("404");
            result.setResult(Result.RESULT_FLG.FAIL.getValue());
            result.setData(null);
            result.setMsg("账号或密码错误");
        }
        return result;
    }
}

UserLogin (用户名 用户编码 角色编码)

@Data
public class UserLogin {
    private String userName;
    private String managerAccountNumber;
    private String roleNumber;
}

拦截器

WebAppConfig 和 TokenIntercepter 

@Configurtion
public class WebAppConfig implements WebMvcConfigurer {
    @Autowired
    private TokenIntercepter tokenIntercepter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        System.err.println("先进的拦截");
        registry.addInterceptor(tokenIntercepter).addPathPatterns("/**");
    }
}
@Slf4j
@Component
public class TokenIntercepter implements HandlerInterceptor {
    //过滤的
    private String[] urls={
            "/management-personnel/login/managerLogin",
            "/management-personnel/login/testHttpRequest",
    };

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        String method=request.getMethod();
        if(method.equals("OPTIONS")) {
            System.out.println("啥东西啊");
            return true;
        }
//        log.info("-----------------进入token拦截器-----------------");
        String url=request.getRequestURI();
        String token=request.getHeader("token");

        //System.err.println(token);
        //System.err.println(url);
        //System.err.println(method);
        for(String item:urls){
            if(url.equals(item)){
                return true;
            }
        }
        UserLogin objectFromToken=null;
        try {
            objectFromToken= JwtUtils.getObjectFromToken(token, RasUtils.getPublicKey(publicKeyPath), UserLogin.class);
            System.out.println(objectFromToken);
            if(objectFromToken==null){
                return false;
            }
            else {
                request.setAttribute("managerAccountNumber",objectFromToken.getManagerAccountNumber());
                return true;
            }
        }catch (ExpiredJwtException exception){
            log.info("登录超时了");
            PrintWriter writer=null;
            try {
                writer = response.getWriter();
                Map map=new HashMap(8);
                map.put("code","403.1");
                String string = JSON.toJSONString(map);
                writer.write(string);
                writer.flush();

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(writer!=null){
                    writer.close();
                }
            }
            return false;
        }catch (Exception exception){
            exception.printStackTrace();
            return false;
        }finally {

        }

    }
}

自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorityForRCheck {
    String authority();
}

aop实现自定义注解

@Slf4j
@Component
@Aspect
public class AuthorityAnnotation {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private HttpServletRequest request;
    @Pointcut("@annotation(com.leyou.annotation.AuthorityForRCheck)")
    public void annotationWhere(){
    }



    @Around("annotationWhere()")
    public Object authorityAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("进入aop环绕");
        if(request != null){
            // Do something
            String managerAccountNumber = (String)request.getAttribute("managerAccountNumber");
            if(managerAccountNumber!=null){
                //根据已经登录的管理员编号 获取权限列表
                Set members = redisTemplate.opsForSet().members(managerAccountNumber);
                //获取注解的所需要的权限
                MethodSignature signature = (MethodSignature)joinPoint.getSignature();
                Method method = signature.getMethod();
                AuthorityForRCheck annotation = method.getAnnotation(AuthorityForRCheck.class);
                //比较是否存在
                for (Object member : members) {
                    String authorityNumber=member.toString();
                    if(authorityNumber.equals("main")){
                        return joinPoint.proceed();
                    }
                    if(annotation.authority().equals(authorityNumber)){
                        return joinPoint.proceed();
                    }
                }
                return R.error();
            }else{
                return R.error();
            }
        }else{
            return R.error();
        }
    }
}

controller层的方法

 在需要权限验证的方法上加上自定义注解并设置访问权限,权限编号对应数据库

springboot基于aop redis token实现token验证和权限验证_第4张图片

你可能感兴趣的:(spring,boot,redis,java)