3万字《SpringBoot微服务开发——Shiro(安全)》

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

Disable verbose logging

log4j.logger.org.apache.shiro.util.ThreadContext=WARN

log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

shiro.ini,需要安装ini插件

img

[users]

user ‘root’ with password ‘secret’ and the ‘admin’ role

root = secret, admin

user ‘guest’ with the password ‘guest’ and the ‘guest’ role

guest = guest, guest

user ‘presidentskroob’ with password ‘12345’ ("That’s the same combination on

my luggage!!!" 😉), and role ‘president’

presidentskroob = 12345, president

user ‘darkhelmet’ with password ‘ludicrousspeed’ and roles ‘darklord’ and ‘schwartz’

darkhelmet = ludicrousspeed, darklord, schwartz

user ‘lonestarr’ with password ‘vespa’ and roles ‘goodguy’ and ‘schwartz’

lonestarr = vespa, goodguy, schwartz

-----------------------------------------------------------------------------

Roles with assigned permissions

Each line conforms to the format defined in the

org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc

-----------------------------------------------------------------------------

[roles]

‘admin’ role has all permissions, indicated by the wildcard ‘*’

admin = *

The ‘schwartz’ role can do anything (*) with any lightsaber:

schwartz = lightsaber:*

The ‘goodguy’ role is allowed to ‘drive’ (action) the winnebago (type) with

license plate ‘eagle5’ (instance specific id)

goodguy = winnebago:drive:eagle5

3、测试Quickstart

/*

  • Licensed to the Apache Software Foundation (ASF) under one

  • or more contributor license agreements. See the NOTICE file

  • distributed with this work for additional information

  • regarding copyright ownership. The ASF licenses this file

  • to you under the Apache License, Version 2.0 (the

  • “License”); you may not use this file except in compliance

  • with the License. You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing,

  • software distributed under the License is distributed on an

  • “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

  • KIND, either express or implied. See the License for the

  • specific language governing permissions and limitations

  • under the License.

*/

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.*;

import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.session.Session;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

/**

  • Simple Quickstart application showing how to use Shiro’s API.

  • @since 0.9 RC2

*/

public class Quickstart {

//日志

private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

public static void main(String[] args) {

// The easiest way to create a Shiro SecurityManager with configured

// realms, users, roles and permissions is to use the simple INI config.

// We’ll do that by using a factory that can ingest a .ini file and

// return a SecurityManager instance:

// Use the shiro.ini file at the root of the classpath

// (file: and url: prefixes load from files and urls respectively):

Factory factory = new IniSecurityManagerFactory(“classpath:shiro.ini”);

SecurityManager securityManager = factory.getInstance();

// for this simple example quickstart, make the SecurityManager

// accessible as a JVM singleton. Most applications wouldn’t do this

// and instead rely on their container configuration or web.xml for

// webapps. That is outside the scope of this simple quickstart, so

// we’ll just do the bare minimum so you can continue to get a feel

// for things.

SecurityUtils.setSecurityManager(securityManager);

// Now that a simple Shiro environment is set up, let’s see what you can do:

// get the currently executing user:

//获取当前的用户对象 Subject

Subject currentUser = SecurityUtils.getSubject();

// Do some stuff with a Session (no need for a web or EJB container!!!)

//通过当前对象获取当前用户的Session

Session session = currentUser.getSession();

session.setAttribute(“someKey”, “aValue”);

//将aValue的session保存在someKey中

String value = (String) session.getAttribute(“someKey”);

if (value.equals(“aValue”)) {

log.info(“Subject==》session [” + value + “]”);

}

// let’s login the current user so we can check against roles and permissions:

//判断当前的用户是否被认证

if (!currentUser.isAuthenticated()) {

//Token 令牌

UsernamePasswordToken token = new UsernamePasswordToken(“lonestarr”, “vespa”);

token.setRememberMe(true);

try {

currentUser.login(token); //执行 登录操作

} catch (UnknownAccountException uae) {//未知账号

log.info("There is no user with username of " + token.getPrincipal());

} catch (IncorrectCredentialsException ice) {//密码错误

log.info(“Password for account " + token.getPrincipal() + " was incorrect!”);

} catch (LockedAccountException lae) { //锁定账号

log.info("The account for username " + token.getPrincipal() + " is locked. " +

“Please contact your administrator to unlock it.”);

}

// … catch more exceptions here (maybe custom ones specific to your application?

catch (AuthenticationException ae) { //认证异常

//unexpected condition? error?

}

}

//say who they are:

//print their identifying principal (in this case, a username):

//获取当前用户信息

log.info(“User [” + currentUser.getPrincipal() + “] logged in successfully.”);

//test a role:

//测试角色 判断当前用户是什么角色

if (currentUser.hasRole(“schwartz”)) {

log.info(“May the Schwartz be with you!”);

} else {

log.info(“Hello, mere mortal.”);

}

//test a typed permission (not instance-level)

//简单 粗粒度

if (currentUser.isPermitted(“lightsaber:wield”)) {

log.info(“You may use a lightsaber ring. Use it wisely.”);

} else {

log.info(“Sorry, lightsaber rings are for schwartz masters only.”);

}

//细粒度

//a (very powerful) Instance Level permission:

if (currentUser.isPermitted(“winnebago:drive:eagle5”)) {

log.info("You are permitted to ‘drive’ the winnebago with license plate (id) ‘eagle5’. " +

“Here are the keys - have fun!”);

} else {

log.info(“Sorry, you aren’t allowed to drive the ‘eagle5’ winnebago!”);

}

//all done - log out!

//注销

currentUser.logout();

//结束

System.exit(0);

}

}

img

这些功能在Spring-Secutiry都有

//获取当前的用户对象 Subject

Subject currentUser = SecurityUtils.getSubject();

//通过当前对象获取当前用户的Session

Session session = currentUser.getSession();

//判断当前的用户是否被认证

currentUser.isAuthenticated()

//获取当前用户信息

currentUser.getPrincipal()

//测试角色 判断当前用户是什么角色

currentUser.hasRole(“schwartz”)

currentUser.isPermitted(“lightsaber:wield”)

//注销

currentUser.logout();

6、SpringBoot整合Shiro环境搭建

导入相关依赖

org.apache.shiro

shiro-core

1.8.0

org.apache.shiro

shiro-spring

1.8.0

org.thymeleaf

thymeleaf-spring5

org.thymeleaf.extras

thymeleaf-extras-java8time

测试SpringBoot环境是否搭建成功

Title

首页


add

update

controller层

@RequestMapping({“/”,“/index”})

public String ToIndex(Model model){

model.addAttribute(“msg”,“hello Shiro”);

return “index”;

}

img

测试SpringBoot整合Shiro环境搭建成功

Shiro配置

img

package com.kk.config;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

//自定义的 UserRealm extends AuthorizingRealm

public class UserRealm extends AuthorizingRealm {

//授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println(“执行科=》授权doGetAuthorizationInfo”);

return null;

}

//认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println(“执行科=》认证doGetAuthenticationInfo”);

return null;

}

}

ShiroConfig

package com.kk.config;

import org.apache.shiro.mgt.DefaultSecurityManager;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

import java.util.LinkedHashMap;

import java.util.Map;

@Configuration

public class ShiroConfig {

//ShiroFilterFactortBean 第三步

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(“securityManager”) DefaultWebSecurityManager defaultWebSecurityManager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

//设置安全管理器

bean.setSecurityManager(defaultWebSecurityManager);

return bean;

}

//DefaultWebSecurityManager 第二步

@Bean(name = “securityManager”)

public DefaultWebSecurityManager getDefaultwebSecurityManager(@Qualifier(“userRealm”) UserRealm userRealm) {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//.关联UserReaLm

securityManager.setRealm(userRealm);

return securityManager;

}

//创建 realm 对象 需要自定义 第一步

@Bean

public UserRealm userRealm() {

return new UserRealm();

}

}

MyController

package com.kk.controller;

import org.springframework.jmx.export.annotation.ManagedResource;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

public class MyController {

@RequestMapping({“/”,“/index”})

public String ToIndex(Model model){

model.addAttribute(“msg”,“hello Shiro”);

return “index”;

}

@RequestMapping(“/user/add”)

public String add(){

return “user/add”;

}

@RequestMapping(“/user/update”)

public String update(){

return “user/update”;

}

@RequestMapping(“/toLogin”)

public String toLogin(){

return “login”;

}

}

index.html

Title

首页


add

update

add.html

Title

add

update.html

Title

update

7、shiro实现登录拦截

package com.kk.config;

import org.apache.shiro.mgt.DefaultSecurityManager;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

import java.util.LinkedHashMap;

import java.util.Map;

@Configuration

public class ShiroConfig {

//ShiroFilterFactortBean 第三步

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(“securityManager”) DefaultWebSecurityManager defaultWebSecurityManager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

//设置安全管理器

bean.setSecurityManager(defaultWebSecurityManager);

//添加shiro的内置过滤器

/*

anon : 无需认证就可以访问

authc:必须认证才能访问

user:必须拥有 记住我 功能才能用

perms 拥有某个资源的权限才能访问

role 拥有某个角色的权限才能访问

*/

Map<String, String> filterMap = new LinkedHashMap<>();

filterMap.put(“/user/add”, “authc”);

filterMap.put(“/user/update”, “authc”);

bean.setFilterChainDefinitionMap(filterMap);

//如果没有权限 ,则跳转到登陆页面

bean.setLoginUrl(“/toLogin”);

return bean;

}

//DefaultWebSecurityManager 第二步

@Bean(name = “securityManager”)

public DefaultWebSecurityManager getDefaultwebSecurityManager(@Qualifier(“userRealm”) UserRealm userRealm) {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//.关联UserReaLm

securityManager.setRealm(userRealm);

return securityManager;

}

//创建 realm 对象 需要自定义 第一步

@Bean

public UserRealm userRealm() {

return new UserRealm();

}

}

login.html

Title

登陆


用户名:

密码:

MyController

@RequestMapping(“/toLogin”)

public String toLogin(){

return “login”;

}

8、Shiro实现用户认证

MyController

@RequestMapping(“/login”)

public String login(String username,String password,Model model){

//获取当前的用户

Subject subject = SecurityUtils.getSubject();

//封装登陆用户的登陆数据

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

try{

subject.login(token);//执行登陆方法,如果没有异常就说明ok

return “index”;

}catch (UnknownAccountException e){//用户名不存在

model.addAttribute(“msg”,“用户名错误”);

return “login”;

}catch (IncorrectCredentialsException e){//密码不存在

model.addAttribute(“msg”,“密码错误”);

return “login”;

}

}

login.html

Title

登陆


用户名:

密码:

UserRealm

//认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

System.out.println(“执行了=》认证doGetAuthenticationInfo”);

//用户名 密码 —数据库

String name=“root”;

String password=“123456”;

UsernamePasswordToken userToken = (UsernamePasswordToken) token;

if (!userToken.getUsername().equals(name)){

return null;//抛出异常UnknownAccountException

}

//密码认证 shiro

return new SimpleAuthenticationInfo(“”,password,“”);

}

9、Shiro整合Mybatis

导入依赖

mysql

mysql-connector-java

log4j

log4j

1.2.17

com.alibaba

druid

1.2.6

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.2.0

2.连接数据库application.yml

spring:

datasource:

username: root

password: 981204

#?serverTimezone=UTC解决时区的报错

url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

driver-class-name: com.mysql.jdbc.Driver

type: com.alibaba.druid.pool.DruidDataSource

#Spring Boot 默认是不注入这些属性值的,需要自己绑定

#druid 数据源专有配置

initialSize: 5

minIdle: 5

maxActive: 20

maxWait: 60000

timeBetweenEvictionRunsMillis: 60000

minEvictableIdleTimeMillis: 300000

validationQuery: SELECT 1 FROM DUAL

testWhileIdle: true

testOnBorrow: false

testOnReturn: false

poolPreparedStatements: true

#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入

#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority

#则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j

filters: stat,wall,log4j

maxPoolPreparedStatementPerConnectionSize: 20

useGlobalDataSourceStat: true

connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3.设置application.properties

整合mybatis

mybatis.type-aliases-package=com.kk.pojo

mybatis.mapper-locations=classpath:mapper/*.xml

4.实体类

package com.kk.pojo;

public class User {

private int id;

private String name;

private String pwd;

public User() {

}

public User(int id, String name, String pwd) {

this.id = id;

this.name = name;

this.pwd = pwd;

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getPwd() {

return pwd;

}

public void setPwd(String pwd) {

this.pwd = pwd;

}

@Override

public String toString() {

return “User{” +

“id=” + id +

“, name='” + name + ‘’’ +

“, pwd='” + pwd + ‘’’ +

‘}’;

}

}

5.UserMapper

package com.kk.mapper;

import org.apache.ibatis.annotations.Mapper;

import org.springframework.stereotype.Repository;

@Repository

@Mapper

public class UserMapper {

public User queryUserByName(String name);

}

6.UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>

select * from mybatis.user where name = #{name}

7.UserService

public interface UserService {

public User queryUserByName(String name);

}

8.UserServiceImpl

package com.kk.service;

import org.springframework.stereotype.Service;

@Service

public class UserServiceImp implements UserService{

@Autowired

UserMapper userMapper;

@Override

public User queryUserByName(String name) {

return userMapper.queryUserByName(name);

}

}

9.测试查出对应User

@SpringBootTest

class ShiroSpringbootApplicationTests {

@Autowired

UserServiceImpl userService;

@Test

void contextLoads() {

System.out.println(userService.queryUserByName(“雏田”));

}

}

10、测试成功后 将数据绑定到安全配置中,再次启动配置

package com.kk.config;

import com.kk.pojo.User;

import com.kk.service.UserService;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.*;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.apache.shiro.subject.Subject;

import org.springframework.beans.factory.annotation.Autowired;

//自定义的 UserRealm extends AuthorizingRealm

public class UserRealm extends AuthorizingRealm {

@Autowired

UserService userService;

//授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println(“执行了=》授权doGetAuthorizationInfo”);

return null;

}

//认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

System.out.println(“执行了=》认证doGetAuthenticationInfo”);

UsernamePasswordToken userToken = (UsernamePasswordToken) token;

//连接真实数据库

User user = userService.queryUserByName(userToken.getUsername());

if (user==null){ //没有这个用户

return null; //抛出异常UnknownAccountException

}

//密码认证 shiro

//可以加密 MD5加密 MD5盐值加密

return new SimpleAuthenticationInfo(“”,user.getPwd(),“”);

}

}

10、Shiro请求授权实现

1、授权正常的情况下,没有授权会跳转到未授权页面

package com.kk.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

import java.util.Map;

@Configuration

public class ShiroConfig {

//ShiroFilterFactoryBean:3

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(“securityManager”) DefaultWebSecurityManager defaultWebSecurityManager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

//设置安全管理器

bean.setSecurityManager(defaultWebSecurityManager);

/*anon:无需认 证就可以访问

authc:必须认证了 才能让问

user:.必须拥有 记住我功能才能用

perms:

拥有对某个资源的权限才能访间:

role:拥有某 个角色权限才能访问

*/

Map<String, String> filterMap = new LinkedHashMap<>();

//授权,正常的情况下,没有授权会跳转到未授权页面

filterMap.put(“/user/add”, “perms[user:add]”);

filterMap.put(“/user/update”, “perms[user:update]”);

//第二个为权限,只有persm=user:add/user:update] 才可能进入相应的页面

filterMap.put(“/user/*”, “authc”);

//filterMap.put(“/user/add”, “authc”);

//filterMap.put(“/user/update”, “authc”);

bean.setFilterChainDefinitionMap(filterMap);

//设置获录的请求

bean.setLoginUrl(“/toLogin”);

//未授权页面

bean. setUnauthorizedUrl(“/noauth”);

return bean;

}

//Dafaul tWebSecurityManager:2

@Bean(name = “securityManager”)

public DefaultWebSecurityManager getDefaultwebSecurityManager(@Qualifier(“userRealm”) UserRealm userRealm) {

总结:绘上一张Kakfa架构思维大纲脑图(xmind)

image

其实关于Kafka,能问的问题实在是太多了,扒了几天,最终筛选出44问:基础篇17问、进阶篇15问、高级篇12问,个个直戳痛点,不知道如果你不着急看答案,又能答出几个呢?

若是对Kafka的知识还回忆不起来,不妨先看我手绘的知识总结脑图(xmind不能上传,文章里用的是图片版)进行整体架构的梳理

梳理了知识,刷完了面试,如若你还想进一步的深入学习解读kafka以及源码,那么接下来的这份《手写“kafka”》将会是个不错的选择。

  • Kafka入门

  • 为什么选择Kafka

  • Kafka的安装、管理和配置

  • Kafka的集群

  • 第一个Kafka程序

  • Kafka的生产者

  • Kafka的消费者

  • 深入理解Kafka

  • 可靠的数据传递

  • Spring和Kafka的整合

  • SpringBoot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

image

image

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
lass ShiroConfig {

//ShiroFilterFactoryBean:3

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier(“securityManager”) DefaultWebSecurityManager defaultWebSecurityManager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

//设置安全管理器

bean.setSecurityManager(defaultWebSecurityManager);

/*anon:无需认 证就可以访问

authc:必须认证了 才能让问

user:.必须拥有 记住我功能才能用

perms:

拥有对某个资源的权限才能访间:

role:拥有某 个角色权限才能访问

*/

Map<String, String> filterMap = new LinkedHashMap<>();

//授权,正常的情况下,没有授权会跳转到未授权页面

filterMap.put(“/user/add”, “perms[user:add]”);

filterMap.put(“/user/update”, “perms[user:update]”);

//第二个为权限,只有persm=user:add/user:update] 才可能进入相应的页面

filterMap.put(“/user/*”, “authc”);

//filterMap.put(“/user/add”, “authc”);

//filterMap.put(“/user/update”, “authc”);

bean.setFilterChainDefinitionMap(filterMap);

//设置获录的请求

bean.setLoginUrl(“/toLogin”);

//未授权页面

bean. setUnauthorizedUrl(“/noauth”);

return bean;

}

//Dafaul tWebSecurityManager:2

@Bean(name = “securityManager”)

public DefaultWebSecurityManager getDefaultwebSecurityManager(@Qualifier(“userRealm”) UserRealm userRealm) {

总结:绘上一张Kakfa架构思维大纲脑图(xmind)

[外链图片转存中…(img-QKDlQwHO-1714464142294)]

其实关于Kafka,能问的问题实在是太多了,扒了几天,最终筛选出44问:基础篇17问、进阶篇15问、高级篇12问,个个直戳痛点,不知道如果你不着急看答案,又能答出几个呢?

若是对Kafka的知识还回忆不起来,不妨先看我手绘的知识总结脑图(xmind不能上传,文章里用的是图片版)进行整体架构的梳理

梳理了知识,刷完了面试,如若你还想进一步的深入学习解读kafka以及源码,那么接下来的这份《手写“kafka”》将会是个不错的选择。

  • Kafka入门

  • 为什么选择Kafka

  • Kafka的安装、管理和配置

  • Kafka的集群

  • 第一个Kafka程序

  • Kafka的生产者

  • Kafka的消费者

  • 深入理解Kafka

  • 可靠的数据传递

  • Spring和Kafka的整合

  • SpringBoot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-GAFyLLdG-1714464142295)]

[外链图片转存中…(img-TrBZjBgL-1714464142295)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值