SSH综合项目实战(快递) -- day12 shiro授权的方法、菜单功能添加

一、在自定义的realm中进行授权

1、在spring配置文件中,加上访问动作类方法需要的权限


2、在realm中给用户进行授权

	/**
	 * 授权的方法
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//为当前用户授权,只需要将用户的权限添加到info对象中就可以了
		/*
		 * 为当前用户授予area的权限,area对应的是spring配置文件中,指定的访问的动作需要的权限字符串,
		 * 		即: /aeraAction_pageQuery.action = perms["area"]
		 */
		info.addStringPermission("area");
		//TODO 后期授权的时候需要根据登录的用户,从数据库中获取当前用户的权限
		return info;
	}

二、使用shiro的方法注解方式进行权限控制

1、在spring文件中配置开启shiro的注解支持


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
	xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap"
	xsi:schemaLocation="
						http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
						http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
						http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
						http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
						http://www.springframework.org/schema/data/jpa 
						http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
						http://cxf.apache.org/bindings/soap 
						http://cxf.apache.org/schemas/configuration/soap.xsd
						http://cxf.apache.org/jaxws 
						http://cxf.apache.org/schemas/jaxws.xsd
						">

	<!-- 连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
		<property name="jdbcUrl" value="jdbc:oracle:thin:@192.168.37.128:1521:ORCL" />
		<property name="user" value="heima_64" />
		<property name="password" value="heima_64" />
	</bean>
	<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
		<property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" 
		value="jdbc:mysql:///test" /> <property name="user" value="root" /> <property 
		name="password" value="root" /> </bean> -->

	<!-- spring整合JPA -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!-- 指定扫描的包,存放实体类 -->
		<property name="packagesToScan" value="com.itheima.bos.domain" />
		<!-- 指定持久层提供者为Hibernate -->
		<property name="persistenceProvider">
			<bean class="org.hibernate.ejb.HibernatePersistence" />
		</property>
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<!-- 自动建表 -->
				<property name="generateDdl" value="true" />
				<property name="database" value="ORACLE" />
				<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
				<property name="showSql" value="true" />
			</bean>
		</property>
		<property name="jpaDialect">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
		</property>
	</bean>

	<!-- 事务管理器 -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!-- 组件扫描 @Server @Controller @Repository -->
	<context:component-scan base-package="com.itheima.bos.service,com.itheima.bos.web" />

	<!-- 整合 spring data jpa -->
	<jpa:repositories base-package="com.itheima.bos.dao" />

	<!-- 注册CRM服务的客户端代理对象,用于通过Webservice访问CRM服务 -->
	<jaxws:client id="crmClient"
		address="http://localhost:8081/crm/webservice/customer" serviceClass="com.itheima.crm.service.CustomerService"></jaxws:client>

	<bean id="orderServiceImpl"
		class="com.itheima.bos.service.take_delivery.impl.OrderServiceImpl"></bean>

	<!-- 注册订单服务 -->
	<jaxws:server id="orderService" address="/order">
		<jaxws:serviceBean>
			<ref bean="orderServiceImpl" />
		</jaxws:serviceBean>
	</jaxws:server>

	<!-- 注册shiro框架的bean(过滤器) ,作用是创建shiro框架相关的一些过滤器,每个过滤器进行不同的权限校验 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 注入shiro框架核心对象,安全管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 
			private String loginUrl; 登录页面 
			private String successUrl; 录成功后跳转页面 
			private String unauthorizedUrl;权限不足时的提示页面 -->
		<property name="loginUrl" value="/login.html" />
		<property name="successUrl" value="/index.html" />
		<property name="unauthorizedUrl" value="/unauthorized.html" />
		<!-- 指定URL拦截规则 -->
		<property name="filterChainDefinitions">
			<!-- 
				authc:代表shiro框架提供的一个过滤器,这个过滤器用于判断当前用户是否已经完成认证, 
							如果当前用户已经认证,就放行,如果当前用户没有认证,跳转到登录页面 
				anon:代表shiro框架提供的一个过滤器,允许匿名访问 ,即可以不登录访问
				**:代表目录下的所有文件及子目录
			-->
			<value>
				/css/* = anon
				/images/* = anon
				/js/** = anon
				/validatecode.jsp* = anon
				/userAction_login.action = anon
				/aeraAction_pageQuery.action = perms["area"]
				/** = authc
			</value>
		</property>
	</bean>

	<bean id="bosRealm" class="com.itheima.bos.shiro.BosRealm" />

	<!-- 注册安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="bosRealm" />
	</bean>
	
	<!-- 事务注解支持,spring整合shiro时候,需要在事务管理器上加上强制使用代理的配置 -->
	<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
	
	<!-- 基于spring自动代理方式为Service类创建代理对象,实现权限控制 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
		<!-- 强制使用cglib方式创建代理对象 -->
		<property name="proxyTargetClass" value="true"></property>
	</bean>
	
	<!-- 切面 -->
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager"></property>
	</bean>
</beans>

2、在service方法上使用注解


3、要想用户能够进行删除快递员操作,需要进行授权


三、使用shiro的标签进行权限控制

1、在页面引入shiro的标签库

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>


2、在realm中给给用户赋予角色


3、在页面中使用标签


四、总结shiro提供的权限控制方式

1、URL过滤器拦截实现权限控制

底层基于shiro框架提供的过滤器,拦截客户端发送的请求,实现权限校验,体现在spring的配置文件中

2、方法注解方式实现权限控制

底层基于cglib代理技术,为Service类创建代理对象,由代理对象实现权限校验,体现在Service类的方法上


3、标签方式实现权限控制

底层基于标签技术,动态展示页面中的元素,体现在jsp页面上


4、编程方式实现权限控制(了解)

框架提供了相应的方法,直接判断当前用户的权限


五、实现用户注销功能

1、改造index.html页面


2、编写UserAction中用户注销的方法

	/**
	 * 用户注销的方法
	 * @return
	 */
	@Action(value="", results={
			@Result(name="userAction_logout", type="redirect", location="/login.html")
	})
	public String logout(){
		//使用shiro提供的用户退出方法进行退出操作
		SecurityUtils.getSubject().logout();
		return SUCCESS;
	}

六、菜单数据管理

1、导入菜单基础数据

-- ----------------------------
-- Records of T_MENU
-- ----------------------------
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('11', '基础档案', null, '1', null, null);
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('111', '基础档案设置', 'pages/base/archives.html', '1', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('112', '收派标准', 'pages/base/standard.html', '2', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('113', '班车管理', 'pages/base/vehicle.html', '3', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('114', '快递员设置/替班', 'pages/base/courier.html', '4', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('115', '区域设置', 'pages/base/area.html', '5', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('116', '管理分区', 'pages/base/sub_area.html', '6', null, '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('117',
   '管理定区/调度排班',
   'pages/base/fixed_area.html',
   '7',
   null,
   '11');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('118', '收派时间管理', 'pages/base/take_time.html', '8', null, '11');

INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('12', '受理', null, '2', null, null);
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('121', '业务受理', 'pages/take_delivery/order.html', '1', null, '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('122',
   '运单快速录入',
   'pages/take_delivery/waybill_quick.html',
   '2',
   null,
   '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('123', '运单录入', 'pages/take_delivery/waybill.html', '3', null, '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('124',
   '运单导入',
   'pages/take_delivery/waybill_import.html',
   '4',
   null,
   '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('125',
   '运单管理',
   'pages/take_delivery/waybill_manage.html',
   '5',
   null,
   '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('126',
   '异调运单',
   'pages/take_delivery/different_waybill.html',
   '6',
   null,
   '12');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('127',
   '运单打印',
   'pages/take_delivery/waybill_print.html',
   '7',
   null,
   '12');

INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('13', '调度', null, '3', null, null);
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('131',
   '查台转单',
   'pages/take_delivery/change_work_order.html',
   '1',
   null,
   '13');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('132',
   '人工调度',
   'pages/take_delivery/dispatcher.html',
   '2',
   null,
   '13');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('133',
   '取派调度监控',
   'pages/take_delivery/dispatcher_monitor.html',
   '3',
   null,
   '13');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('134',
   '签收录入',
   'pages/take_delivery/sign_input.html',
   '4',
   null,
   '13');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('135',
   '取消签收申请',
   'pages/take_delivery/sign_cancel.html',
   '5',
   null,
   '13');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('136',
   '宣传任务',
   'pages/take_delivery/promotion.html',
   '6',
   null,
   '13');

INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('14', '分拣管理', null, '4', null, null);
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('141', '入库', 'pages/transit/in_storage.html', '1', null, '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('142', '出库', 'pages/transit/out_storage.html', '2', null, '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('143', '盘库', 'pages/transit/make_storage.html', '3', null, '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('144', '合包', 'pages/transit/compose_package.html', '4', null, '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('145',
   '到达时间录入',
   'pages/transit/arrive_time.html',
   '5',
   null,
   '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('146',
   '出入库查询',
   'pages/transit/in_out_search.html',
   '6',
   null,
   '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('147', '库存查询', 'pages/transit/remain_search.html', '7', null, '14');
INSERT INTO T_MENU
  (C_ID, C_NAME, C_PAGE, C_PRIORITY, C_DESCRIPTION, C_PID)
VALUES
  ('148', '合包查询', 'pages/transit/package_search.html', '8', null, '14');

2、使用combotree展示菜单数据

(1)、改造父菜单展示代码


	<td>父菜单项</td>
	<td>
		<input name="parentMenu.id" class="easyui-combotree" 
			data-options="url:'../../menuAction_listajax.action',required:true"
				sytle="width:200px;" />
	</td>

(2)、改造Menu实体类中的方法


	@OneToMany(mappedBy = "parentMenu",fetch=FetchType.EAGER)//查询menu时候查询出子菜单集合,立即加载
	private Set<Menu> childrenMenus = new HashSet<Menu>();

	@ManyToOne
	@JoinColumn(name = "C_PID")
	private Menu parentMenu;

	/**
	 * 为menu转为json添加getText()和getChildren()方法
	 */
	public String getText(){
		return name;
	}
	
	public Set<Menu> getChildren(){
		return childrenMenus;
	}

(3)、编写MenuAction

package com.itheima.bos.web.action.system;

import java.util.List;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.system.Menu;
import com.itheima.bos.service.system.MenuService;
import com.itheima.bos.web.action.common.CommonAction;

/**
 * 菜单管理动作类
 * @author Administrator
 *
 */
@Controller
@Namespace("/")
@ParentPackage("struts-default")
@Scope("prototype")
public class MenuAction extends CommonAction<Menu> {

	@Autowired
	private MenuService service;
	
	@Action(value="menuAction_listajax")
	public String listajax(){
		List<Menu> list = service.findTopMenu();
		this.list2Json(list, new String[]{"roles","childrenMenus","parentMenu"});
		return NONE;
	}
}

(4)、编写MenuService

package com.itheima.bos.service.system.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.bos.dao.system.MenuDao;
import com.itheima.bos.domain.system.Menu;
import com.itheima.bos.service.system.MenuService;

@Service
@Transactional
public class MenuServiceImpl implements MenuService {

	@Autowired
	private MenuDao dao;
	
	/**
	 * 查询顶层菜单
	 */
	public List<Menu> findTopMenu() {
		return dao.findByParentMenuIsNull();
	}

}

(5)、编写MenuDao

package com.itheima.bos.dao.system;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.itheima.bos.domain.system.Menu;

public interface MenuDao extends JpaRepository<Menu, Integer> {

	public List<Menu> findByParentMenuIsNull();

}

(6)、页面效果展示


3、菜单添加功能实现

(1)、修改menu_add.html页面按钮提交事件


(2)、编写后台action代码

	/**
	 * 菜单添加
	 */
	@Action(value="menuAction_add", results={
			@Result(name="success", type="redirect", location="/pages/system/menu.html")
	})
	public String add(){
		service.saveMenu(this.getModel());
		return SUCCESS;
	}

(3)、编写service层代码

	/**
	 * 保存菜单
	 */
	public void saveMenu(Menu model) {
		if(model.getParentMenu() == null || model.getParentMenu().getId() == 0 ){
			model.setParentMenu(null);
		}
		dao.save(model);
	}

4、菜单的分页查询

(1)、menu.html中分页url


(2)、action中代码

	/**
	 * 菜单分页查询
	 * @throws Exception 
	 */
	@Action(value="menuAction_pageQuery")
	public String pageQuery() throws Exception{
		//Menu实体类中有page属性,接收参数的时候,如果分页中的page和实体类中的page重复,默认会把参数封装到实体类中的page中,因此要取出来
		page = Integer.valueOf(this.getModel().getPage());
		if(page == 0){
			page = 1;
		}
		//创建一个pageable对象,封装分页参数,用于分页查询
		Pageable pageable = new PageRequest(page - 1, rows);
		Page<Menu> page = service.pageQuery(pageable);
		this.page2Json(page, new String[]{"roles","childrenMenus","parentMenu"});
		return NONE;
	}

(3)、service层代码

	/**
	 * 菜单的分页查询方法
	 */
	public Page<Menu> pageQuery(Pageable pageable) {
		return dao.findAll(pageable);
	}

七、权限数据管理

1、权限数据的分页查询

(1)、调整权限展示页面的url


(2)、编写PermissionAciton代码

package com.itheima.bos.web.action.system;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.system.Permission;
import com.itheima.bos.service.system.PermissionService;
import com.itheima.bos.web.action.common.CommonAction;

/**
 * 权限管理动作类
 * @author Administrator
 *
 */
@Controller
@Namespace("/")
@ParentPackage("struts-default")
@Scope("prototype")
public class PermissionAction extends CommonAction<Permission> {

	@Autowired
	private PermissionService service;
	
	/**
	 * 权限分页查询
	 * @return
	 */
	@Action(value="permissionAction_pageQuery")
	public String pageQuery(){
		Pageable pageable = new PageRequest(page - 1, rows);
		Page<Permission> page = service.findByPage(pageable);
		this.page2Json(page, new String[]{"roles"});
		return NONE;
	}
}

(3)、编写PermissionService代码

package com.itheima.bos.service.system.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.bos.dao.system.PermissionDao;
import com.itheima.bos.domain.system.Permission;
import com.itheima.bos.service.system.PermissionService;

/**
 * 权限管理service层
 * @author Administrator
 *
 */
@Service
@Transactional
public class PermissionServiceImpl implements PermissionService {

	@Autowired
	private PermissionDao dao;
	/**
	 * 分页查询方法
	 */
	public Page<Permission> findByPage(Pageable pageable) {
		return dao.findAll(pageable);
	}

}

(4)、编写PermissionDao代码

package com.itheima.bos.dao.system;

import org.springframework.data.jpa.repository.JpaRepository;

import com.itheima.bos.domain.system.Permission;

public interface PermissionDao extends JpaRepository<Permission, Integer> {

}

2、权限添加

(1)、修改permission_add.html页面


(2)、编写action代码

	/**
	 * 权限添加
	 */
	@Action(value="permissionAction_add",results={
			@Result(name="success", type="redirect", location="/pages/system/permission.html")
	})
	public String add(){
		service.save(this.getModel());
		return SUCCESS;
	}

(3)、编写Service代码

	/**
	 * 权限添加
	 */
	public void save(Permission model) {
		dao.save(model);
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值