第一步:SpirngBoot 项目

1.1 建库脚本

/*
 Navicat MySQL Data Transfer

 Source Server         : 192.168.1.73
 Source Server Type    : MySQL
 Source Server Version : 80015
 Source Host           : 192.168.1.73:3306
 Source Schema         : auth_shrio

 Target Server Type    : MySQL
 Target Server Version : 80015
 File Encoding         : 65001

 Date: 02/12/2020 18:45:00
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for cost
-- ----------------------------
DROP TABLE IF EXISTS `cost`;
CREATE TABLE `cost`  (
  `cost_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `house_id` int(11) NOT NULL COMMENT '房屋ID',
  `create_date` date NOT NULL COMMENT '创建日期',
  `money` int(11) NOT NULL DEFAULT 0 COMMENT '金额',
  `status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态:1 未支付,2:已支付',
  PRIMARY KEY (`cost_id`) USING BTREE,
  INDEX `FK_cost_house`(`house_id`) USING BTREE,
  CONSTRAINT `FK_cost_house` FOREIGN KEY (`house_id`) REFERENCES `house` (`house_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '费用信息表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of cost
-- ----------------------------
INSERT INTO `cost` VALUES (1, 2, '2020-12-02', 3360, 1);

-- ----------------------------
-- Table structure for house
-- ----------------------------
DROP TABLE IF EXISTS `house`;
CREATE TABLE `house`  (
  `house_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `house_number` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '房屋编号',
  `area` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '区域',
  `house_address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房屋地址',
  `house_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房屋类型',
  `acreage` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '房屋面积',
  `peroples` int(10) NULL DEFAULT NULL COMMENT '可容纳人数',
  `rent` int(10) NULL DEFAULT NULL COMMENT '房屋租金',
  `status` tinyint(4) NULL DEFAULT NULL COMMENT '房屋状态:1:已租、2:待租',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  PRIMARY KEY (`house_id`) USING BTREE,
  INDEX `FK_house_user`(`user_id`) USING BTREE,
  CONSTRAINT `FK_house_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '房屋信息表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of house
-- ----------------------------
INSERT INTO `house` VALUES (1, '0001', '广东省宝安区麻布新村', '105号13-1101', '1', '28.35平方', 3, 1800, 1, 3);
INSERT INTO `house` VALUES (2, '0002', '广东省宝安区麻布新村', '105号13-1102', '1', '44.2平方', 5, 3400, 1, 1);
INSERT INTO `house` VALUES (4, '0003', '广东省宝安区麻布新村', '105号13-1103', '1', '38.35平方', 3, 2800, 1, 1);

-- ----------------------------
-- Table structure for house_lease
-- ----------------------------
DROP TABLE IF EXISTS `house_lease`;
CREATE TABLE `house_lease`  (
  `house_id` int(11) NOT NULL COMMENT '房屋ID',
  `lease_id` int(11) NOT NULL COMMENT '租赁者ID',
  PRIMARY KEY (`house_id`, `lease_id`) USING BTREE,
  INDEX `FK_house_lease_lease`(`lease_id`) USING BTREE,
  CONSTRAINT `FK_house_lease_house` FOREIGN KEY (`house_id`) REFERENCES `house` (`house_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `FK_house_lease_lease` FOREIGN KEY (`lease_id`) REFERENCES `lease` (`lease_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '房屋租赁信息表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of house_lease
-- ----------------------------
INSERT INTO `house_lease` VALUES (1, 1);
INSERT INTO `house_lease` VALUES (2, 1);

-- ----------------------------
-- Table structure for lease
-- ----------------------------
DROP TABLE IF EXISTS `lease`;
CREATE TABLE `lease`  (
  `lease_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `real_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租赁者姓名',
  `id_card` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租赁者身份证号码',
  `sex` tinyint(4) NOT NULL COMMENT '性别:1 男 2 女',
  `telphone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '电话',
  `hometown` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '租赁者籍贯',
  `status` tinyint(4) NOT NULL COMMENT '状态:1:租房中  2:未租房',
  `start_date` date NOT NULL COMMENT '租赁开始时间',
  `end_date` date NOT NULL COMMENT '租赁结束时间',
  `sign_date` date NOT NULL COMMENT '租赁签订时间',
  PRIMARY KEY (`lease_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '租赁者信息表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of lease
-- ----------------------------
INSERT INTO `lease` VALUES (1, '周志刚', '430921199112205454', 1, '13265740591', '湖南', 1, '2015-10-01', '2025-01-01', '2020-12-02');

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `permission_id` int(11) NOT NULL,
  `permision_component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `permision_icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `permision_parent_id` int(11) NULL DEFAULT NULL,
  `permission_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `permission_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `permission_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`permission_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (1, '首页', 'Admin', '/admin', 'el-icon-s-home', 'AdminIndex', 0);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (2, '运行情况', 'DashBoard', '/admin/dashboard', NULL, 'dashboard/Index', 1);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (3, '用户管理', 'User', '/admin', 'el-icon-user', 'AdminIndex', 0);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (4, '用户信息', 'Profile', '/admin/user/Profile', NULL, 'user/Profile', 3);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (5, '角色配置', 'Role', '/admin/user/Role', NULL, 'user/Role', 3);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (6, '测试菜单', 'ArticleManage', '/admin', 'el-icon-edit', 'AdminIndex', 0);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (7, '菜单列表1', 'ArticleList', '/admin/test/Test1', NULL, 'test/Test1', 6);
INSERT INTO `permission`(permission_id,permission_zh,permission_name,permission_path,permision_icon,permision_component,permision_parent_id) VALUES (8, '菜单列表2', 'ArticleEdit', '/admin/test/Test2', NULL, 'test/Test2', 6);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `role_id` int(11) NOT NULL,
  `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'svip');
INSERT INTO `role` VALUES (2, 'vip');
INSERT INTO `role` VALUES (3, 'p');
INSERT INTO `role` VALUES (4, 'admin');

-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`  (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`, `permission_id`) USING BTREE,
  INDEX `FKf8yllw1ecvwqy3ehyxawqa1qp`(`permission_id`) USING BTREE,
  CONSTRAINT `FKa6jx8n8xkesmjmv6jqug6bg68` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `FKf8yllw1ecvwqy3ehyxawqa1qp` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`permission_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (1, 4);
INSERT INTO `role_permission` VALUES (1, 5);
INSERT INTO `role_permission` VALUES (1, 6);
INSERT INTO `role_permission` VALUES (1, 7);
INSERT INTO `role_permission` VALUES (1, 8);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 2);

-- ----------------------------
-- Table structure for token_relation
-- ----------------------------
DROP TABLE IF EXISTS `token_relation`;
CREATE TABLE `token_relation`  (
  `relation_sid` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'token凭证',
  PRIMARY KEY (`relation_sid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of token_relation
-- ----------------------------
INSERT INTO `token_relation` VALUES (3, 'Jack', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDY5NTkzNzAsInVzZXJuYW1lIjoiSmFjayJ9.m3OZ1w2YQLmI0cE9oK79_pZbXnON_-e5YMbZLPtdyrU');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `user_id` int(11) NOT NULL COMMENT '主键',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '账户',
  `telephone` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号码',
  `email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱地址',
  `guid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '唯一码',
  `real_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实姓名',
  `address` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '123', 'Jack', '123', '123@163.com', NULL, NULL, NULL);
INSERT INTO `user` VALUES (2, '123', 'Rose', '1234', '1234@163.com', NULL, NULL, NULL);
INSERT INTO `user` VALUES (3, '123', 'Paul', '12345', '12345@163.com', NULL, NULL, NULL);
INSERT INTO `user` VALUES (4, '123', 'Jane', '123456', '123456@163.com', NULL, '简', '中国广东省深圳市盐田区');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`, `role_id`) USING BTREE,
  INDEX `FKa68196081fvovjhkek5m97n3y`(`role_id`) USING BTREE,
  CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (3, 3);
INSERT INTO `user_role` VALUES (4, 4);

-- ----------------------------
-- Table structure for verification
-- ----------------------------
DROP TABLE IF EXISTS `verification`;
CREATE TABLE `verification`  (
  `verification_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `verification_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '验证码',
  `status` tinyint(255) NULL DEFAULT NULL COMMENT '状态:1:未使用,2:已使用',
  `create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建日期',
  `fail_date` datetime(0) NULL DEFAULT NULL COMMENT '失效日期',
  PRIMARY KEY (`verification_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

1.2 SpringBoot项目源码

1.2.1 实体层定义:

package com.zzg.entity;

import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class Cost {
	
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
    private Integer costId;
	
	 @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示house不能为空。删除房屋,不影响用户
	 @JoinColumn(name="house_id")//设置在house表中的关联字段(外键)
	 private House house;//所属房屋
	 
	 private Date createDate;
	 
	 private Integer money;
	 
	 private Integer status;

}
package com.zzg.entity;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class House {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
	private Integer houseId;

	private String houseNumber;

	private String area;

	private String houseAddress;

	private String houseType;

	private String acreage;

	private Integer peroples;

	private Integer rent;

	private Integer status;

	@ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH}, optional = false) // 可选属性optional=false,表示house不能为空。删除房屋,不影响用户
	@JoinColumn(name = "user_id") // 设置在house表中的关联字段(外键)
	private User user;// 所属房屋


	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "house_lease", joinColumns = {
			@JoinColumn(name = "house_id", referencedColumnName = "houseId") }, inverseJoinColumns = {
					@JoinColumn(name = "lease_id", referencedColumnName = "leaseId") })
	private Set<Lease> leases;

}
package com.zzg.entity;

import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
public class Lease {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
	private Integer leaseId;

	private String realName;

	private String idCard;

	private Integer sex;

	private String telphone;

	private String hometown;

	private Integer status;

	private Date startDate;

	private Date endDate;

	private Date signDate;

//	@ManyToMany(fetch = FetchType.EAGER)
//	@JoinTable(name = "house_lease", joinColumns = {
//			@JoinColumn(name = "lease_id", referencedColumnName = "leaseId") }, inverseJoinColumns = {
//					@JoinColumn(name = "house_id", referencedColumnName = "houseId") })
//	private Set<House> houses;
}
package com.zzg.entity;

import javax.persistence.Entity;
import javax.persistence.Id;

import lombok.Getter;
import lombok.Setter;

/**
 * 
 * @author zzg
 *
 */
@Getter
@Setter
@Entity
public class Permission {

    @Id
    private Integer permissionId;
    private String permissionName;
    private String permissionZh;
    private String permissionPath;
    private String permisionIcon;
    private String permisionComponent;
    private Integer permisionParentId;
    
//    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)   
//    @JoinColumn(name="permision_parent_id")  
//    private Permission parent;
//    // 子菜单项
//    @OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.EAGER)  
//    private Set<Permission> child  = new HashSet<Permission>() ;
}
package com.zzg.entity;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.Set;

/**
 * 
 * @author zzg
 *
 */

@Getter
@Setter
@Entity
public class Role {

    @Id
    private Integer roleId;
    private String roleName;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "role_permission",
            joinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")},
            inverseJoinColumns = {@JoinColumn(name = "PERMISSION_ID", referencedColumnName = "permissionId")})
    private Set<Permission> permissions;
}
package com.zzg.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity(name="token_relation")
public class TokenRelation {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
	private Integer relationSid;
	
	private String username;
	
	private String token;
	
}
package com.zzg.entity;

import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;

import lombok.Getter;
import lombok.Setter;

/**
 * 
 * @author zzg
 *
 */
@Getter
@Setter
@Entity
public class User {
    @Id
    private Integer userId;

    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_role",
            joinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "userId")},
            inverseJoinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")})
    private Set<Role> roles;
    
    private String telephone;
    
    private String email;
    
    private String guid;
    
    private String realName;
    
    private String address;
}
package com.zzg.entity;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity(name="verification")
public class Verification {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
	private Integer verificationId;
	
	private String verificationCode;
	
	private Integer status;
	
	private Date createDate;
	
	private Date failDate;
	

}

1.2.2 dao层定义:

package com.zzg.dao;

import javax.transaction.Transactional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.zzg.entity.Cost;

@Repository
public interface CostRepository extends JpaRepository<Cost, Integer>, JpaSpecificationExecutor<Cost> {

	/**
	 * 原生SQL
	 * @param houseId
	 * @param costId
	 * @return
	 */
	@Transactional
	@Modifying
	@Query(nativeQuery = true,value="update cost u set u.house_id = ?1 where u.cost_id = ?2")
	int modifyHouseId(Integer houseId, Integer costId);

}
package com.zzg.dao;

import javax.transaction.Transactional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.zzg.entity.House;

@Repository
public interface HouseRepository extends JpaRepository<House, Integer>, JpaSpecificationExecutor<House> {
	/**
	 * 原生SQL
	 * @param houseId
	 * @param costId
	 * @return
	 */
	@Transactional
	@Modifying
	@Query(nativeQuery = true,value="update house u set u.user_id = ?1 where u.house_id = ?2")
	int modifyUserId(Integer userId, Integer houseId);
}
package com.zzg.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.zzg.entity.Lease;

@Repository
public interface LeaseRepository extends JpaRepository<Lease, Integer>, JpaSpecificationExecutor<Lease> {
	
}
package com.zzg.dao;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.zzg.entity.Permission;

/**
 * 
 * @author zzg
 *
 */
@Repository
public interface PermissionRepository extends JpaRepository<Permission, Integer>, JpaSpecificationExecutor<Permission> {
}
package com.zzg.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.zzg.entity.Role;

/**
 * 
 * @author zzg
 *
 */
@Repository
public interface RoleRepository extends JpaRepository<Role, Integer>, JpaSpecificationExecutor<Role> {
	Role findByRoleId(Integer userId);
}
package com.zzg.dao;

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

import com.zzg.entity.TokenRelation;

@Repository("tokenRelationRepository")
public interface TokenRelationRepository extends JpaRepository<TokenRelation, Integer> {
	TokenRelation findByUsername(String username);
	
	TokenRelation findByToken(String token);
}
package com.zzg.dao;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

import com.zzg.entity.User;


/**
 * 
 * @author zzg
 *
 */
@Repository
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
    User findByUsername(String username);

    User findByUserId(Integer userId);
    
    User findByEmail(String email);
    
    User findByTelephone(String telephone);
}
package com.zzg.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.zzg.entity.Verification;

@Repository
public interface VerificationRepository extends JpaRepository<Verification, Integer> {
	Verification findByVerificationCode(String verificationCode);
}

1.2.3 controller层定义:

package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.zzg.common.AbstractController;
import com.zzg.dao.CostRepository;
import com.zzg.entity.Cost;

@RestController
public class CostController extends AbstractController {
	@Autowired
	CostRepository costRepository;
	
	@RequestMapping(value = "/cost/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody Cost cost) {
		costRepository.save(cost);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段和多对多表关系,产生原因:由于@Many-To-One
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/cost/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody Cost cost) {
		Optional<Cost> old = costRepository.findById(cost.getCostId());
		if(old.isPresent()){
			Cost oldCost = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(cost, oldCost);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            costRepository.save(oldCost);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	/**
	 * 基于原生SQL 功能实现
	 * @param houseId
	 * @param costId
	 * @return
	 */
	@RequestMapping(value = "/cost/houseId", method = RequestMethod.GET)
	@ResponseBody
	public Map updateHouseId(Integer houseId,Integer costId) {
		costRepository.modifyHouseId(houseId, costId);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	
	
	@RequestMapping(value = "/cost/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
		
		Page<Cost> stus = costRepository.findAll(new Specification<Cost>(){
			@Override
			public Predicate toPredicate(Root<Cost> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	@RequestMapping(value = "/cost/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		costRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}

}
package com.zzg.controller;

import java.util.HashMap;
import java.util.Map;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DataController {
	@RequiresPermissions({"save"}) //没有的话 AuthorizationException
    @PostMapping("/data/save")
 	@ResponseBody
    public Map<String, Object> save(@RequestHeader("token")String token) {
        System.out.println("save");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有save的权力");
        return map;
    }//f603cd4348b8f1d41226e3f555d392bd

    @RequiresPermissions({"delete"}) //没有的话 AuthorizationException
    @DeleteMapping("/data/delete")
    @ResponseBody
    public Map<String, Object> delete(@RequestHeader("token")String token) {
        System.out.println("delete");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有delete的权力");
        return map;
    }

    @RequiresPermissions({"update"}) //没有的话 AuthorizationException
    @PutMapping("/data/update")
    @ResponseBody
    public Map<String, Object> update(@RequestHeader("token")String token) {
        System.out.println("update");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有update的权力");
        return map;
    }

    @RequiresPermissions({"select"}) //没有的话 AuthorizationException
    @GetMapping("/data/select")
    @ResponseBody
    public Map<String, Object> select(@RequestHeader("token")String token) {
        System.out.println("select");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有select的权力");
        return map;
    }

    @RequiresRoles({"vip"}) //没有的话 AuthorizationException
    @GetMapping("/data/vip")
    @ResponseBody
    public Map<String, Object> vip(@RequestHeader("token")String token) {
        System.out.println("vip");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有VIP角色");
        return map;
    }
    @RequiresRoles({"svip"}) //没有的话 AuthorizationException
    @GetMapping("/data/svip")
    @ResponseBody
    public Map<String, Object> svip(@RequestHeader("token")String token) {
        System.out.println("svip");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有SVIP角色");
        return map;
    }
    @RequiresRoles({"p"}) //没有的话 AuthorizationException
    @GetMapping("/data/p")
    @ResponseBody
    public Map<String, Object> p(@RequestHeader("token")String token) {
        System.out.println("p");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", 200);
        map.put("msg", "当前用户有P角色");
        return map;
    }
}
package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.AbstractController;
import com.zzg.dao.HouseRepository;
import com.zzg.entity.House;


@RestController
public class HouseController extends AbstractController{
	@Autowired
	HouseRepository houseRepository;
	
	@RequestMapping(value = "/house/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody House house) {
		houseRepository.save(house);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段和多对多表关系,产生原因:由于@Many-To-One
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/house/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody House house) {
		Optional<House> old = houseRepository.findById(house.getHouseId());
		if(old.isPresent()){
			House oldHouse = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(house, oldHouse);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            houseRepository.save(oldHouse);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	
	/**
	 * 基于原生SQL 功能实现
	 * @param houseId
	 * @param costId
	 * @return
	 */
	@RequestMapping(value = "/house/userId", method = RequestMethod.GET)
	@ResponseBody
	public Map updateUserId(Integer userId,Integer houseId) {
		houseRepository.modifyUserId(userId, houseId);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	@RequestMapping(value = "/house/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
		
		Page<House> stus = houseRepository.findAll(new Specification<House>(){
			@Override
			public Predicate toPredicate(Root<House> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				if(paramter.get("houseId") != null){
					 predicateList.add(criteriaBuilder.equal(root.get("houseId"), paramter.get("houseId")));
				}
				if(paramter.get("houseNumber") != null){
					 predicateList.add(criteriaBuilder.equal(root.get("houseNumber"), paramter.get("houseNumber")));
				}
				if(paramter.get("area") != null){
					 predicateList.add(criteriaBuilder.like(root.get("area"), "%" + paramter.get("area") +"%"));
				}
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	@RequestMapping(value = "/house/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		houseRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}
	


}
package com.zzg.controller;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.zzg.redis.RedisUtils;
import com.zzg.util.KaptchaUtil;

@RestController
public class KaptchaController {
	public static final String VRIFY_CODE = "VRIFYCODE";
	
	@GetMapping("/sys/kaptcha")
	public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
			throws Exception {
		
		byte[] captchaChallengeAsJpeg = null;
		ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
		try {
			// 代码方式创建:DefaultKaptcha
			KaptchaUtil single = KaptchaUtil.getInstance();
			DefaultKaptcha defaultKaptcha = single.produce();
			// 生产验证码字符串并保存到session中
			String createText = defaultKaptcha.createText();
			// 基于session 方式
			//httpServletRequest.getSession().setAttribute(VRIFY_CODE, createText);
			// 基于redis 方式
			RedisUtils.hPut(VRIFY_CODE, createText, createText);
			// 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
			BufferedImage challenge = defaultKaptcha.createImage(createText);
			ImageIO.write(challenge, "jpg", jpegOutputStream);
		} catch (IllegalArgumentException e) {
			httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
 
		// 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
		captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
		httpServletResponse.setHeader("Cache-Control", "no-store");
		httpServletResponse.setHeader("Pragma", "no-cache");
		httpServletResponse.setDateHeader("Expires", 0);
		httpServletResponse.setContentType("image/jpeg");
		ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
		responseOutputStream.write(captchaChallengeAsJpeg);
		responseOutputStream.flush();
		responseOutputStream.close();
	}

}
package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.AbstractController;
import com.zzg.dao.LeaseRepository;
import com.zzg.entity.Lease;

@RestController
public class LeaseController extends AbstractController {
	@Autowired
	private LeaseRepository leaseRepository;
	
	@RequestMapping(value = "/lease/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody Lease lease) {
		leaseRepository.save(lease);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段和和多对多表关系
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/lease/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody Lease lease) {
		Optional<Lease> old = leaseRepository.findById(lease.getLeaseId());
		if(old.isPresent()){
			Lease oldLease = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(lease, oldLease);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            leaseRepository.save(oldLease);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	

	@RequestMapping(value = "/lease/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
		
		Page<Lease> stus = leaseRepository.findAll(new Specification<Lease>(){
			@Override
			public Predicate toPredicate(Root<Lease> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	@RequestMapping(value = "/lease/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		leaseRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}
	
	

}
package com.zzg.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.druid.util.StringUtils;
import com.zzg.dao.TokenRelationRepository;
import com.zzg.dao.UserRepository;
import com.zzg.dao.VerificationRepository;
import com.zzg.entity.TokenRelation;
import com.zzg.entity.User;
import com.zzg.entity.Verification;
import com.zzg.redis.RedisUtils;
import com.zzg.util.JWTUtil;

@RestController
public class LoginController {
	public static final String VRIFY_CODE = "VRIFYCODE";
	// 过期时间 24 小时
	private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;
	@Autowired
	UserRepository userRepository;
	@Autowired
	TokenRelationRepository tokenRelationRepository;
	@Autowired
	VerificationRepository verificationRepository;
	
	/**
     * 登录
     */
	@CrossOrigin
    @PostMapping("/common/login")
    @ResponseBody
    public Map<String, Object> commonLogin(@RequestBody Map<String, Object> parame) {
        Map<String, Object> result = new HashMap<>();
        String username = (String) parame.get("username");
        String password = (String) parame.get("password");
        //用户信息
        User user = userRepository.findByUsername(username);
        //账号不存在、密码错误
        if (user == null || !user.getPassword().equals(password)) {
            result.put("status", 400);
            result.put("msg", "账号或密码有误");
        } else {
            //生成token,并保存到redis
        	String token = JWTUtil.createToken(username);
        	RedisUtils.set(username,token);
        	RedisUtils.expire(username, EXPIRE_TIME);
        	// user->token 关系保存到mysql 中
        	TokenRelation relation = tokenRelationRepository.findByUsername(username);
        	if(relation == null){
        		relation = new TokenRelation();
        		relation.setUsername(username);
            	relation.setToken(token);
        	} else {
        		relation.setToken(token);
        	}
        	tokenRelationRepository.save(relation);
        	result.put("uid", user.getUserId());
        	result.put("username", username);
        	result.put("token", token);
            result.put("status", 200);
            result.put("msg", "登陆成功");
           
        }
        return result;
    }
	
	
	/**
     * 登录
     */
    @PostMapping("/sys/login")
    @ResponseBody
    public Map<String, Object> login(@RequestParam("username")String username, @RequestParam("password")String password, @RequestParam("captcha")String captcha) {
        Map<String, Object> result = new HashMap<>();
        
        //校验验证码
        //基于session的验证码
        // String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute(VRIFY_CODE);
        //基于redis的验证码
        String redisCaptcha = (String) RedisUtils.hGet(VRIFY_CODE, captcha);
        if (StringUtils.isEmpty(redisCaptcha)) {
        	 result.put("status", 400);
             result.put("msg", "验证码有误");
             return result;
        }
        //用户信息
        User user = userRepository.findByUsername(username);
        //账号不存在、密码错误
        if (user == null || !user.getPassword().equals(password)) {
            result.put("status", 400);
            result.put("msg", "账号或密码有误");
        } else {
            //生成token,并保存到redis
        	String token = JWTUtil.createToken(username);
        	RedisUtils.set(username,token);
        	RedisUtils.expire(username, EXPIRE_TIME);
        	// user->token 关系保存到mysql 中
        	TokenRelation relation = tokenRelationRepository.findByUsername(username);
        	if(relation == null){
        		relation = new TokenRelation();
        		relation.setUsername(username);
            	relation.setToken(token);
        	} else {
        		relation.setToken(token);
        	}
        	tokenRelationRepository.save(relation);
        	result.put("token", token);
            result.put("status", 200);
            result.put("msg", "登陆成功");
            // 移除验证码
            RedisUtils.hDelete(VRIFY_CODE, captcha);
        }
        return result;
    }
    
    public boolean validateCode(String code){
    	 Verification verification = verificationRepository.findByVerificationCode(code);
    	 if(verification != null){
    		 
    	 }
    	 return false;
    }
    
    /**
     * 邮箱登录
     */
    @PostMapping("/email/login")
    @ResponseBody
    public Map<String, Object> emailLogin(@RequestParam("email")String email, @RequestParam("code")String code) {
        Map<String, Object> result = new HashMap<>();
        
      
        //用户信息
        User user = userRepository.findByEmail(email);
        Verification verification = verificationRepository.findByVerificationCode(code);
        //账号不存在、验证码错误
        if (user == null || verification == null) {
            result.put("status", 400);
            result.put("msg", "邮箱有误");
        } else {
            //生成token,并保存到redis
        	String token = JWTUtil.createToken(user.getUsername());
        	RedisUtils.set(user.getUsername(),token);
        	RedisUtils.expire(user.getUsername(), EXPIRE_TIME);
        	// user->token 关系保存到mysql 中
        	TokenRelation relation = tokenRelationRepository.findByUsername(user.getUsername());
        	if(relation == null){
        		relation = new TokenRelation();
        		relation.setUsername(user.getUsername());
            	relation.setToken(token);
        	} else {
        		relation.setToken(token);
        	}
        	tokenRelationRepository.save(relation);
        	result.put("token", token);
            result.put("status", 200);
            result.put("msg", "登陆成功");
            
            // 验证码移除
            verificationRepository.deleteById(verification.getVerificationId());
           
        }
        return result;
    }
    
    /**
     * 邮箱登录
     */
    @PostMapping("/phone/login")
    @ResponseBody
    public Map<String, Object> phoneLogin(@RequestParam("phone")String phone, @RequestParam("code")String code) {
        Map<String, Object> result = new HashMap<>();
        
      
        //用户信息
        User user = userRepository.findByTelephone(phone);
        Verification verification = verificationRepository.findByVerificationCode(code);
        //账号不存在、验证码错误
        if (user == null || verification == null) {
            result.put("status", 400);
            result.put("msg", "手机号码有误");
        } else {
            //生成token,并保存到redis
        	String token = JWTUtil.createToken(user.getUsername());
        	RedisUtils.set(user.getUsername(),token);
        	RedisUtils.expire(user.getUsername(), EXPIRE_TIME);
        	// user->token 关系保存到mysql 中
        	TokenRelation relation = tokenRelationRepository.findByUsername(user.getUsername());
        	if(relation == null){
        		relation = new TokenRelation();
        		relation.setUsername(user.getUsername());
            	relation.setToken(token);
        	} else {
        		relation.setToken(token);
        	}
        	tokenRelationRepository.save(relation);
        	result.put("token", token);
            result.put("status", 200);
            result.put("msg", "登陆成功");
            
            // 验证码移除
            verificationRepository.deleteById(verification.getVerificationId());
           
        }
        return result;
    }
    
    
    /**
     * 退出
     */
   
    @PostMapping("/sys/logout")
    public Map<String, Object> logout(@RequestHeader("token")String token) {
        Map<String, Object> result = new HashMap<>();
        String username = null;
        TokenRelation relation = tokenRelationRepository.findByToken(token);
        if(relation != null){
        	username = relation.getUsername();
        	tokenRelationRepository.deleteById(relation.getRelationSid());
        }
        if(!StringUtils.isEmpty(username)){
        	// redis 移除token
            RedisUtils.del(username);
        }
       
        result.put("status", 200);
        result.put("msg", "您已安全退出系统");
        return result;
    }
}
package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.AbstractController;
import com.zzg.dao.PermissionRepository;
import com.zzg.dao.RoleRepository;
import com.zzg.dao.UserRepository;
import com.zzg.entity.Permission;
import com.zzg.entity.Role;
import com.zzg.entity.User;
import com.zzg.vo.PermissionVo;

@RestController
public class PermissionController extends AbstractController{
	@Autowired
	private PermissionRepository permissionRepository;
	@Autowired
	private UserRepository userRepository;
	@Autowired
	private RoleRepository roleRepository;
	
	
	@RequestMapping(value = "/permission/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody Permission permission) {
		permissionRepository.save(permission);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/permission/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody Permission permission) {
		Optional<Permission> old = permissionRepository.findById(permission.getPermissionId());
		if(old.isPresent()){
			Permission oldPermission = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(permission, oldPermission);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            permissionRepository.save(oldPermission);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	@RequestMapping(value = "/permission/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
		
		Page<Permission> stus = permissionRepository.findAll(new Specification<Permission>(){
			@Override
			public Predicate toPredicate(Root<Permission> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	/**
	 * 查询用户指定权限
	 * @param uid
	 * @return
	 */
	@RequestMapping(value = "/permission/{uid}", method = RequestMethod.GET)
	@ResponseBody
	public Map findPermission(@PathVariable Integer uid) {
		List<PermissionVo> permissionsVos = new ArrayList<PermissionVo>();
		
		List<Permission> permissions = new ArrayList<Permission>();
		//显示第1页每页显示3条
		User user = userRepository.findByUserId(uid);
		
		user.getRoles().stream().forEach(item->{
        	Role role = roleRepository.findByRoleId(item.getRoleId());
        	if(!CollectionUtils.isEmpty(role.getPermissions())){
        		for(Permission permission : role.getPermissions()){
        			if(permission.getPermisionParentId() == null){
        				permissions.add(permission);
        			}
        		}
        	}
        });
		
	
		
		
		permissions.stream().forEach(item ->{
			 
			 /**
			  * Spring Data JPA 动态查询条件
			  */
			 Specification<Permission> specification = new Specification<Permission>() {
	             @Override
	            public Predicate toPredicate(Root<Permission> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
	                List<Predicate> predicates = new ArrayList<Predicate>();
	                if(null != item.getPermissionId()) {
	                    predicates.add(cb.equal(root.<Integer>get("permisionParentId"), item.getPermissionId()));
	                }
	                return cq.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
	            }
	        };
	        List<Permission> childPermission = permissionRepository.findAll(specification);
	        List<PermissionVo> childPermissionVO = new ArrayList<PermissionVo>();
	        for(Permission permission : childPermission){
	        	 PermissionVo vo = new PermissionVo();
	        	 BeanUtils.copyProperties(permission, vo);
	        	 childPermissionVO.add(vo);
	        }
	        
	        PermissionVo vo = new PermissionVo();
	        BeanUtils.copyProperties(item, vo);
	        vo.setChild(childPermissionVO);
	        
	        permissionsVos.add(vo);
	        
		});
		 
		
		// 数据清理
//		Iterator<Permission> iterator = permissions.iterator();
//		while(iterator.hasNext()){
//			Permission permission = iterator.next();
//			if(permission.getPermisionParentId() != null){
//				iterator.remove();
//			}
//		}
		
		

//		for(Permission permission : permissions){
//			if(permission.getParent() != null){
//				permissions.remove(permission);
//			}
//		}
				
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", permissionsVos);
		return map;
	}
	
	@RequestMapping(value = "/permission/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		permissionRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}

}
package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.AbstractController;
import com.zzg.dao.RoleRepository;
import com.zzg.entity.Role;

@RestController
public class RoleController extends AbstractController {
	@Autowired
	private RoleRepository roleRepository;
	
	@RequestMapping(value = "/role/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody Role role) {
		roleRepository.save(role);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/role/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody Role role) {
		Optional<Role> old = roleRepository.findById(role.getRoleId());
		if(old.isPresent()){
			Role oldRole = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(role, oldRole);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            roleRepository.save(oldRole);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	@RequestMapping(value = "/role/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
	
		Page<Role> stus = roleRepository.findAll(new Specification<Role>(){
			@Override
			public Predicate toPredicate(Root<Role> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	@RequestMapping(value = "/role/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		roleRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}

}
package com.zzg.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.AbstractController;
import com.zzg.dao.UserRepository;
import com.zzg.entity.User;

@RestController
public class UserController extends AbstractController {
	@Autowired
	private UserRepository userRepository;
	
	@RequestMapping(value = "/user/save", method = RequestMethod.POST)
	@ResponseBody
	public Map insert(@RequestBody User user) {
		userRepository.save(user);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "新增成功");
		return map;
	}
	
	/**
	 * 动态更新:仅限于更新单表字段
	 * @param house
	 * @return
	 */
	@RequestMapping(value = "/user/update", method = RequestMethod.POST)
	@ResponseBody
	public Map update(@RequestBody User user) {
		Optional<User> old = userRepository.findById(user.getUserId());
		if(old.isPresent()){
			User oldUser = old.get();
            //将传过来的 house 中的非NULL属性值复制到 oldHouse 中
            copyPropertiesIgnoreNull(user, oldUser);
            //将得到的新的 oldHouse 对象重新保存到数据库,因为数据库中已经存在该记录
            //所以JPA会很智能的 改为更新操作,更新数据库
            userRepository.save(oldUser);
        }
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "更新成功");
		return map;
	}
	
	@RequestMapping(value = "/user/findPage", method = RequestMethod.POST)
	@ResponseBody
	public Map findPage(@RequestBody Map<String, Object> paramter) {
		//显示第1页每页显示3条
		PageRequest pr = super.initPageBounds(paramter);
	
		Page<User> stus = userRepository.findAll(new Specification<User>(){
			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
				// TODO Auto-generated method stub
				List<Predicate> predicateList = new ArrayList<>();
				
				return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		}, pr);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "查询成功");
		map.put("date", stus);
		return map;
	}
	
	@RequestMapping(value = "/user/delete/{id}", method = RequestMethod.GET)
	@ResponseBody
	public Map delete(@PathVariable("id") Integer id) {
		userRepository.deleteById(id);
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("message", "删除成功");
		return map;
	}

}
package com.zzg.controller;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zzg.dao.VerificationRepository;
import com.zzg.entity.Verification;
import com.zzg.util.HttpContextUtil;

@RestController
public class VerificationController {
	// 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
    
    @Autowired
    VerificationRepository verificationRepository;
    
    public Verification getVerification(String code){
    	Date date = new Date();
    	Verification verification = new Verification();
    	verification.setCreateDate(date);
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(date);
    	calendar.set(Calendar.MINUTE,  calendar.get(Calendar.MINUTE) + 15);//分钟+15
    	verification.setFailDate(calendar.getTime());
    	verification.setStatus(1);
    	verification.setVerificationCode(code);
    	return verification;
    }
   
    
	@GetMapping("/sys/verification")
	public void defaultVerification(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
	throws Exception {
		Verification verification = this.getVerification("123456");
		verificationRepository.save(verification);
		
		httpServletResponse.setContentType("application/json;charset=utf-8");
		httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
		httpServletResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
		httpServletResponse.setCharacterEncoding("UTF-8");
        //设置编码,否则中文字符在重定向时会变为空字符串
        Map<String, Object> result = new HashMap<>();
        result.put("status", 200);
        result.put("code", verification.getVerificationCode());
        result.put("msg", "验证码发送成功");
        String json = MAPPER.writeValueAsString(result);
        httpServletResponse.getWriter().print(json);
		
	}
}

1.2.4 通用定义:

package com.zzg.common.constant;

public class WebAppConstants {
	public static final String LIMIT = "limit";
	public static final String PAGE = "page";
}
package com.zzg.common;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.data.domain.PageRequest;

import com.zzg.common.constant.WebAppConstants;

public abstract class AbstractController {
	/**
	 * 参数分页参数转换校验:现在仅支持:get请求、参数格式:?page=1&limit=10
	 * 
	 * @param param
	 * @return
	 */
	protected PageRequest initPageBounds(Map<String, Object> paramter) {
		SimpleTypeConverter converter = new SimpleTypeConverter();
		// 页码、分页大小初始化设置值
		int page = 0;
		int limit = 10;
		if (paramter.get(WebAppConstants.PAGE) != null) {
			page = converter.convertIfNecessary(paramter.get(WebAppConstants.PAGE), int.class);
		}
		if (paramter.get(WebAppConstants.LIMIT) != null) {
			limit = converter.convertIfNecessary(paramter.get(WebAppConstants.LIMIT), int.class);
		}
		PageRequest pb = new PageRequest(page, limit);
		return pb;
	}

	public static void copyPropertiesIgnoreNull(Object src, Object target) {
		BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
	}

	public static String[] getNullPropertyNames(Object source) {
		final BeanWrapper src = new BeanWrapperImpl(source);
		java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

		Set<String> emptyNames = new HashSet<String>();
		for (java.beans.PropertyDescriptor pd : pds) {
			Object srcValue = src.getPropertyValue(pd.getName());
			if (srcValue == null)
				emptyNames.add(pd.getName());
		}
		String[] result = new String[emptyNames.size()];
		return emptyNames.toArray(result);
	}
}

1.2.5 配置对象定义:GlobalCorsConfig :系统跨域对象配置、ShiroConfig :apache shrio 用户登入鉴权配置

package com.zzg.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class GlobalCorsConfig {
	@Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
          //放行哪些原始域
          config.addAllowedOrigin("*");
          //是否发送Cookie信息
          config.setAllowCredentials(true);
          //放行哪些原始域(请求方式)
          config.addAllowedMethod("*");
          //放行哪些原始域(头部信息)
          config.addAllowedHeader("*");
          //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
          // config.addExposedHeader("*");
          
          config.addExposedHeader("Content-Type");
          config.addExposedHeader( "X-Requested-With");
          config.addExposedHeader("accept");
          config.addExposedHeader("Origin");
          config.addExposedHeader( "Access-Control-Request-Method");
          config.addExposedHeader("Access-Control-Request-Headers");


        //2.添加映射路径
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }

}
package com.zzg.config;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzg.filter.JWTFilter;
import com.zzg.shrio.MyRealm;

@Configuration
public class ShiroConfig {
	/**
	 * 先走 filter ,然后 filter 如果检测到请求头存在 token,则用 token 去 login,走 Realm 去验证
	 */
	@Bean
	public ShiroFilterFactoryBean factory(org.apache.shiro.mgt.SecurityManager securityManager) {
		ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
		// 添加自己的过滤器并且取名为jwt
		Map<String, Filter> filterMap = new LinkedHashMap<>();
		// 设置自定义的JWT过滤器
		filterMap.put("jwt", new JWTFilter());
		factoryBean.setFilters(filterMap);
		factoryBean.setSecurityManager(securityManager);

		Map<String, String> filterRuleMap = new HashMap<>();
		filterRuleMap.put("/common/login", "anon"); //简单登录接口排除
		filterRuleMap.put("/phone/login", "anon"); //邮箱登录接口排除
		filterRuleMap.put("/email/login", "anon"); //邮箱登录接口排除
		filterRuleMap.put("/sys/login", "anon"); //登录接口排除
		filterRuleMap.put("/sys/logout", "anon"); //登出接口排除
		filterRuleMap.put("/sys/kaptcha", "anon"); //验证码接口排除
		filterRuleMap.put("/sys/verification", "anon"); //验证码接口排除
		// 所有请求通过我们自己的JWT Filter
		filterRuleMap.put("/**", "jwt");
		factoryBean.setFilterChainDefinitionMap(filterRuleMap);
		return factoryBean;
	}

	/**
	 * 注入 securityManager
	 */
	@Bean
	public org.apache.shiro.mgt.SecurityManager securityManager(MyRealm customRealm) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 设置自定义 realm.
		securityManager.setRealm(customRealm);
		/*
		 * 关闭shiro自带的session,详情见文档
		 * http://shiro.apache.org/session-management.html#SessionManagement-
		 * StatelessApplications%28Sessionless%29
		 */
		DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
		DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
		defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
		subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
		securityManager.setSubjectDAO(subjectDAO);
		return securityManager;
	}

	/**
	 * 添加注解支持
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		// 强制使用cglib,防止重复代理和可能引起代理出错的问题
		// https://zhuanlan.zhihu.com/p/29161098
		defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
		return defaultAdvisorAutoProxyCreator;

	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}

	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

}

1.2.6 全局异常定义

package com.zzg.exception;

import java.util.HashMap;
import java.util.Map;

import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
	@ExceptionHandler(value = AuthorizationException.class)
	public Map<String, String> handleException(AuthorizationException e) {
		// e.printStackTrace();
		Map<String, String> result = new HashMap<String, String>();
		result.put("status", "400");
		// 获取错误中中括号的内容
		String message = e.getMessage();
		String msg = message.substring(message.indexOf("[") + 1, message.indexOf("]"));
		// 判断是角色错误还是权限错误
		if (message.contains("role")) {
			result.put("msg", "对不起,您没有" + msg + "角色");
		} else if (message.contains("permission")) {
			result.put("msg", "对不起,您没有" + msg + "权限");
		} else {
			result.put("msg", "对不起,您的权限有误");
		}
		return result;
	}
}

1.2.7 JWTFilter 过滤器定义

package com.zzg.filter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import com.alibaba.druid.util.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zzg.dao.TokenRelationRepository;
import com.zzg.entity.TokenRelation;
import com.zzg.redis.RedisUtils;
import com.zzg.shrio.JWTToken;
import com.zzg.spring.SpringContextUtil;
import com.zzg.util.HttpContextUtil;

public class JWTFilter extends BasicHttpAuthenticationFilter {
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	 // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
   
    
	/**
     * 如果带有 token,则对 token 进行检查,否则直接通过
     * >2 接着执行 isAccessAllowed
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        // 判断请求的请求头是否存在 Token
        if (isLoginAttempt(request, response)) {
            //如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
            // 不正常就会抛出异常
            try {
                // 执行登录
                executeLogin(request, response);
                return true;
            } catch (Exception e) {
                //token 错误
                responseError(response, e.getMessage());
            }
        } else {
        	try{
        	 HttpServletResponse httpResponse = (HttpServletResponse) response;
             httpResponse.setContentType("application/json;charset=utf-8");
             httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
             httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
             httpResponse.setCharacterEncoding("UTF-8");
             //设置编码,否则中文字符在重定向时会变为空字符串
             Map<String, Object> result = new HashMap<>();
             result.put("status", 400);
             result.put("msg", "token 已经注销");
             String json = MAPPER.writeValueAsString(result);
             httpResponse.getWriter().print(json);
             	return false;
        	}catch(IOException e) {
                logger.error(e.getMessage());
            }
        }
        // 如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,
        // 无需检查 token,直接返回 true
        return true;
    }

    /**
     * 判断用户是否想要登入。
     * 检测 header 里面是否包含 Token 字段
     *
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("req.getHeader(Token)"+req.getHeader("Token"));
        String token = req.getHeader("Token");
        if(StringUtils.isEmpty(token)){
        	return false;
        }
        TokenRelationRepository tokenRelationRepository = SpringContextUtil.getBean(TokenRelationRepository.class);
        TokenRelation relation = tokenRelationRepository.findByToken(token);
        if(relation != null){
        	return RedisUtils.get(relation.getUsername()) != null;
        }
        return false;

    }

    /**
     * 执行登陆操作
     *
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Token");

        JWTToken jwtToken = new JWTToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    /**
     *
     * 对跨域提供支持
     *
     * >1 请求最先从这开始执行
     *
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 设置 header key-value
        // httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,aaa");
        // System.out.println(httpServletRequest.getHeader("Origin"));

        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // System.out.println( httpServletRequest.getHeader("Access-Control-Request-Headers"));

        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }

    /**
     * 将非法请求跳转到 /unauthorized/**
     */
    private void responseError(ServletResponse response, String message) {
        try {

            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
            httpResponse.setCharacterEncoding("UTF-8");
            //设置编码,否则中文字符在重定向时会变为空字符串
            Map<String, Object> result = new HashMap<>();
            result.put("status", 500);
            result.put("msg", message);
            String json = MAPPER.writeValueAsString(result);
            httpResponse.getWriter().print(json);
        } catch (IOException e) {

            logger.error(e.getMessage());
        }
    }
}

1.2.8 Redis工具类封装

package com.zzg.redis;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;

import com.zzg.spring.SpringContextUtil;

public class RedisUtils {
	private RedisUtils() {
    }
 
    @SuppressWarnings("unchecked")
    private static RedisTemplate<String, Object> redisTemplate = SpringContextUtil
        .getBean("redisTemplate", RedisTemplate.class);
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public static boolean expire(final String key, final long timeout) {
 
        return expire(key, timeout, TimeUnit.SECONDS);
    }
 
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public static boolean expire(final String key, final long timeout, final TimeUnit unit) {
 
        Boolean ret = redisTemplate.expire(key, timeout, unit);
        return ret != null && ret;
    }
 
    /**
     * 删除单个key
     *
     * @param key 键
     * @return true=删除成功;false=删除失败
     */
    public static boolean del(final String key) {
 
        Boolean ret = redisTemplate.delete(key);
        return ret != null && ret;
    }
 
    /**
     * 删除多个key
     *
     * @param keys 键集合
     * @return 成功删除的个数
     */
    public static long del(final Collection<String> keys) {
 
        Long ret = redisTemplate.delete(keys);
        return ret == null ? 0 : ret;
    }
 
    /**
     * 存入普通对象
     *
     * @param key Redis键
     * @param value 值
     */
    public static void set(final String key, final Object value) {
 
        redisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES);
    }
 
    // 存储普通对象操作
 
    /**
     * 存入普通对象
     *
     * @param key 键
     * @param value 值
     * @param timeout 有效期,单位秒
     */
    public static void set(final String key, final Object value, final long timeout) {
 
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }
 
    /**
     * 获取普通对象
     *
     * @param key 键
     * @return 对象
     */
    public static Object get(final String key) {
 
        return redisTemplate.opsForValue().get(key);
    }
 
    // 存储Hash操作
    /**
     * 删除Hash中指定数据
     *
     * @param key Redis键
     * @param hKey Hash键
     */
    public static void hDelete(final String key, final String hKey) {
        redisTemplate.opsForHash().delete(key, hKey);
    }
 
    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public static void hPut(final String key, final String hKey, final Object value) {
 
        redisTemplate.opsForHash().put(key, hKey, value);
    }
 
    /**
     * 往Hash中存入多个数据
     *
     * @param key Redis键
     * @param values Hash键值对
     */
    public static void hPutAll(final String key, final Map<String, Object> values) {
 
        redisTemplate.opsForHash().putAll(key, values);
    }
 
    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public static Object hGet(final String key, final String hKey) {
 
        return redisTemplate.opsForHash().get(key, hKey);
    }
 
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public static List<Object> hMultiGet(final String key, final Collection<Object> hKeys) {
 
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

 
    // 存储Set相关操作
 
    /**
     * 往Set中存入数据
     *
     * @param key Redis键
     * @param values 值
     * @return 存入的个数
     */
    public static long sSet(final String key, final Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        return count == null ? 0 : count;
    }
 
    /**
     * 删除Set中的数据
     *
     * @param key Redis键
     * @param values 值
     * @return 移除的个数
     */
    public static long sDel(final String key, final Object... values) {
        Long count = redisTemplate.opsForSet().remove(key, values);
        return count == null ? 0 : count;
    }
 
    // 存储List相关操作
 
    /**
     * 往List中存入数据
     *
     * @param key Redis键
     * @param value 数据
     * @return 存入的个数
     */
    public static long lPush(final String key, final Object value) {
        Long count = redisTemplate.opsForList().rightPush(key, value);
        return count == null ? 0 : count;
    }
 
    /**
     * 往List中存入多个数据
     *
     * @param key Redis键
     * @param values 多个数据
     * @return 存入的个数
     */
    public static long lPushAll(final String key, final Collection<Object> values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        return count == null ? 0 : count;
    }
 
    /**
     * 往List中存入多个数据
     *
     * @param key Redis键
     * @param values 多个数据
     * @return 存入的个数
     */
    public static long lPushAll(final String key, final Object... values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        return count == null ? 0 : count;
    }
 
    /**
     * 从List中获取begin到end之间的元素
     *
     * @param key Redis键
     * @param start 开始位置
     * @param end 结束位置(start=0,end=-1表示获取全部元素)
     * @return List对象
     */
    public static List<Object> lGet(final String key, final int start, final int end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

}

1.2.9  Apache Shrio 认证相关类定义

package com.zzg.shrio;

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken{
	/**
	 * 
	 */
	private static final long serialVersionUID = -6747540462673342320L;
	private String token;

    public JWTToken(String token) {
        this.token = token;
    }


    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}
package com.zzg.shrio;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import com.alibaba.druid.util.StringUtils;
import com.zzg.dao.RoleRepository;
import com.zzg.dao.UserRepository;
import com.zzg.entity.Permission;
import com.zzg.entity.Role;
import com.zzg.entity.User;
import com.zzg.util.JWTUtil;

@Component
public class MyRealm extends AuthorizingRealm {
	
	@Autowired
	@Lazy
	private UserRepository userRepository;
	
	@Autowired
	@Lazy
	private RoleRepository roleRepository;

	 /**
     * 必须重写此方法,不然会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }
    
    /**
     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
     */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		  System.out.println(">执行 doGetAuthorizationInfo 权限认证");
	        // principals 传过来的是 token
	        // System.out.println(">principals = "+principals);
	        String username = JWTUtil.getUsername(principals.toString());

	        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

	        //获得该用户角色
	        User user = userRepository.findByUsername(username);

	       

	        Set<String> roleSet = new HashSet<>();
	        Set<String> permissionSet = new HashSet<>();
	        // 需要将 role, permission 封装到 Set 作为 info.setRoles(), info.setStringPermissions() 的参数
	        user.getRoles().stream().forEach(item->{
	        	roleSet.add(item.getRoleName());
	        });
	        user.getRoles().stream().forEach(item->{
	        	Role role = roleRepository.findByRoleId(item.getRoleId());
	        	if(!CollectionUtils.isEmpty(role.getPermissions())){
	        		for(Permission permission : role.getPermissions()){
	        			 permissionSet.add(permission.getPermissionPath());
	        		}
	        	}
	        });
	        // 设置该用户拥有的角色和权限
	        info.setRoles(roleSet);
	        info.setStringPermissions(permissionSet);
	        return info;
	}

	 /**
     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
     */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		// TODO Auto-generated method stub
		  System.out.println(">执行 doGetAuthenticationInfo 身份认证");

	        String token = (String) authenticationToken.getCredentials();
	        System.out.println(">token "+token);
	        // 解密获得 username 用于和数据库进行对比
	        String username = JWTUtil.getUsername(token);

	        if (username == null || !JWTUtil.verify(token, username)) {
	            throw new AuthenticationException("token 认证失败");
	        }

	        // 检查用户是否存在 通过密码来判断
	        User user = userRepository.findByUsername(username);
	        if (StringUtils.isEmpty(user.getPassword())) {
	            throw new AuthenticationException("该用户不存在 ");
	        }

	        System.out.println(">getName "+getName());
	        // return new SimpleAuthenticationInfo(token, token, "MyRealm");
	        return new SimpleAuthenticationInfo(token, token, getName());
	}

}

1.2.10  Spring 工具类

package com.zzg.spring;

import org.springframework.context.ApplicationContext;


public class SpringContextUtil {
	private static ApplicationContext applicationContext;

	public static void setApplicationContext(ApplicationContext applicationContext){
		// TODO Auto-generated method stub
		if(SpringContextUtil.applicationContext == null) {
			SpringContextUtil.applicationContext = applicationContext;
		}
	}

	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	// 通过class 获取bean
	public static <T> T getBean(Class<T> clazz) {
		return applicationContext.getBean(clazz);
	}
	
	// 通过name 获取bean
	public static Object getBean(String name) {
		return applicationContext.getBean(name);
	}
	
	  //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return applicationContext.getBean(name, clazz);
    }
}

1.2.11  系统封装工具类

package com.zzg.util;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class HttpContextUtil {
	public static HttpServletRequest getHttpServletRequest() {
		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
	}

	public static String getDomain() {
		HttpServletRequest request = getHttpServletRequest();
		StringBuffer url = request.getRequestURL();
		return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
	}

	public static String getOrigin() {
		HttpServletRequest request = getHttpServletRequest();
		return request.getHeader("Origin");
	}
}
package com.zzg.util;

import java.util.Date;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

public class JWTUtil {
	// 过期时间 24 小时
	private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;

	// 密钥
	private static final String SECRET = "SHIRO+JWT";

	/**
	 * 登录时通过 loginController 生成 token, 5min后过期
	 *
	 * @param username
	 *            用户名
	 * @return 加密的token
	 */
	public static String createToken(String username) {

		try {
			Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
			Algorithm algorithm = Algorithm.HMAC256(SECRET);
			System.out.println(">algorithm " + algorithm);

			// 返回 token
			// 附带username信息
			return JWT.create().withClaim("username", username)
					// 到期时间
					.withExpiresAt(date)
					// 创建一个新的JWT,并使用给定的算法进行标记
					.sign(algorithm);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 校验 token 是否正确
	 *
	 * @param token
	 *            密钥
	 * @param username
	 *            用户名
	 * @return 是否正确
	 */
	public static boolean verify(String token, String username) {
		System.out.println(">执行 verify");

		try {
			Algorithm algorithm = Algorithm.HMAC256(SECRET);
			// 在token中附带了 username 信息
			JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
			// 验证 token
			verifier.verify(token);
			return true;
		} catch (Exception exception) {
			// 出错就返回 false
			return false;
		}
	}

	/**
	 * 根据token 获取 username 获得token中的信息,无需 secret 解密也能获得
	 *
	 * @return token 中包含的 username
	 */
	public static String getUsername(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim("username").asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}
}
package com.zzg.util;

import java.util.Properties;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;

public class KaptchaUtil {
	private static KaptchaUtil instance;
	 
	private KaptchaUtil() {
	};
 
	public static KaptchaUtil getInstance() {
		if (instance == null) {
			instance = new KaptchaUtil();
		}
		return instance;
	}
 
	/**
	 * 生成DefaultKaptcha 默认配置
	 * @return
	 */
	public DefaultKaptcha produce() {
		Properties properties = new Properties();
		properties.put("kaptcha.border", "no");
		properties.put("kaptcha.border.color", "105,179,90");
		properties.put("kaptcha.textproducer.font.color", "blue");
		properties.put("kaptcha.image.width", "100");
		properties.put("kaptcha.image.height", "50");
		properties.put("kaptcha.textproducer.font.size", "27");
		properties.put("kaptcha.session.key", "code");
		properties.put("kaptcha.textproducer.char.length", "4");
		properties.put("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
		properties.put("kaptcha.textproducer.char.string", "0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ");
		properties.put("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
		properties.put("kaptcha.noise.color", "black");
		properties.put("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
		properties.put("kaptcha.background.clear.from", "185,56,213");
		properties.put("kaptcha.background.clear.to", "white");
		properties.put("kaptcha.textproducer.char.space", "3");
		
		Config config = new Config(properties);
		DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
		defaultKaptcha.setConfig(config);
		return defaultKaptcha;
	}

}

1.2.12  实体对象拓展

package com.zzg.vo;

import java.util.List;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class PermissionVo {
	private Integer permissionId;
	private String permissionName;
	private String permissionZh;
	private String permissionPath;
	private String permisionIcon;
	private String permisionComponent;
	private Integer permisionParentId;
	private List<PermissionVo> child;
}

1.2.13  SpringBoot 程序入口

package com.zzg;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import com.zzg.spring.SpringContextUtil;


@SpringBootApplication
@EntityScan("com.zzg.entity") //Spring-JPA实体扫描
@EnableJpaRepositories(basePackages = { "com.zzg.dao" }) //Spring-JPA Repository扫描
public class ShrioApplication {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		 ApplicationContext applicationContext = SpringApplication.run(ShrioApplication.class, args);
		 SpringContextUtil.setApplicationContext(applicationContext);
	}

}

1.2.14 SpringBoot 程序资源文件(application.yml)

# Tomcat
server:
  tomcat:
    uri-encoding: UTF-8
    max-threads: 1000
    min-spare-threads: 30
  port: 9090
  connection-timeout: 5000ms
  servlet:
    context-path: /shiro
#spring
spring:
  # 数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/auth_shrio?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
      username: root
      password: 123456
      initial-size: 10
      max-active: 100
      min-idle: 10
      max-wait: 60000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
      filter:
        stat:
          log-slow-sql: false
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config:
            multi-statement-allow: true
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    database: mysql
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password: abc123

1.2.15 SpringBoot 程序依赖pom.xml 文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.zzg</groupId>
  <artifactId>lease_sys</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	
	<properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	   <encoding>UTF-8</encoding>
	   <java.version>1.8</java.version>
    </properties>
    
      <dependencies>
        <!--starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--  test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--validation-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--JPA-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--JDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- jwt  -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!--mysql-connector-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- druid-spring-boot-starter -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!-- redis-->
		<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
        <!-- commons-lang -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- apache common 模块 -->
		<!--commons-lang3 工具包 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.9</version>
		</dependency>
		<!--commons-codec 加密工具包 -->
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.10</version>
		</dependency>
		<!--commons-net 网络工具包 -->
		<dependency>
			<groupId>commons-net</groupId>
			<artifactId>commons-net</artifactId>
			<version>3.6</version>
		</dependency>
		<!--common-io 工具包 -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>
		<!--common-collection 工具包 -->
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
			<version>3.2.1</version>
		</dependency>
        <!-- 依赖kaptcha图形生成器  -->
		<dependency>
			<groupId>com.github.penggle</groupId>
			<artifactId>kaptcha</artifactId>
			<version>2.3.2</version>
		</dependency>
    </dependencies>
  
</project>

1.2.15 SpringBoot  项目整体截图:

1.3 Vue项目源码

1.3.1 Vue 项目整体结构图

1.3.2 main.js 函数:安装element-ui、安装axios、集成路由(router)、集成Vuex(store)

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from  './store'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'

Vue.prototype.$axios = axios
Vue.use(ElementUI);
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

1.3.3 App.vue:定义Vue程序入口

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
</style>

1.3.4 utils 文件夹:封装axios 请求(post\get\delete),  封装axios 请求拦截器和响应拦截器 、异步转同步定时函数

import axios from 'axios'
// 设置后端请求地址
axios.defaults.baseURL='http://192.168.1.74:9090/shiro'
// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/json'
// 携带凭证信息
axios.defaults.withCredentials = true

// 添加请求拦截器,在请求头中加token
axios.interceptors.request.use(
    config => {
      if (window.localStorage.getItem('token')) {
        config.headers.token = window.localStorage.getItem('token');
      }
      return config;
    },
    error => {
      return Promise.reject(error);
    });
  
  // 添加响应拦截器,移除token
  axios.interceptors.response.use(
    response =>{
      return response;
    },
    error=>{
      if(error.response){
        switch(error.response.status){
          case 401:
            localStorage.removeItem('token');
            this.$router.push('/login');
        }
      }
    })

/**
 * get方法,对应get请求
 * @param {Stringf} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function get (url, params) {
    return new Promise((resolve, reject) => {
      axios.get(url, {
        params: params
      }).then(res => {
        resolve(res.data)
      }).catch(err => {
        reject(err.data)
      })
    })
  }
  
  /**
   * post方法,对应post请求
   * @param {String} url [请求的url地址]
   * @param {Object} params [请求时携带的参数]
   */
  export function post (url, params) {
    return new Promise((resolve, reject) => {
      axios.post(url, params).then(res => {
        resolve(res.data)
      }).catch(err => {
        reject(err.data)
      })
    })
  }
  
  /**
   * del 方法,对应del 请求
   * @param {String} url [请求的url地址] 
   * @param {Object} params [请求时携带的参数] 
   */
  export function del (url, params) {
    return new Promise((resolve, reject) => {
      axios.delete(url, params).then(res => {
        resolve(res.data)
      }).catch(err => {
        reject(err.data)
      })
    })
  }

  export function waitsleep (method) {
    const start = Number(Date.now())
    return new Promise((resolve) => {
      (function selfRecursion () {
        setTimeout(() => {
          let flag
          if (typeof method === 'function') {
            flag = method()
          }
          if (typeof method === 'number') {
            flag = Number(Date.now()) - start < method
          }
          if (flag) {
            selfRecursion()
          } else {
            resolve()
          }
        }, 10)
      })()
    })
  }

 routerFormat.js :动态加载菜单函数定义

/**
 * 构建动态菜单路由对象
 * @param {Object} routes [后台返回路由数据]
 */
export function formatRoutes (routes) {
    console.log(routes)
    let fmtRoutes = []
    routes.forEach(routes => {
      if (routes.child) {
        routes.child = formatRoutes(routes.child)
      }
  
      let fmtRoute = {
        path: routes.permissionPath,
        component: resolve => {
          require(['@/components/admin/' + routes.permisionComponent + '.vue'], resolve)
        },
        name: routes.permissionName,
        nameZh: routes.permissionZh,
        iconCls: routes.permisionIcon,
        children: routes.child
      }
      fmtRoutes.push(fmtRoute)
    })
    return fmtRoutes
  }
  

initAdmin.js:触发动态加载菜单函数

import {get, waitsleep} from '@/utils/http'
import {formatRoutes} from '../utils/routerFormat'

export default function initAdminMenu (router, store) {
  if (store.state.adminMenus.length > 0) {
    return
  }

  // 定时函数:定时函数实现异步方法模拟同步方法
  waitsleep(() => store.state.uid === -1).then(() => {
    // 动态权限请求
    get('/permission/' + store.state.uid).then(resp => {
    console.log(resp.date)
    console.log(resp.date)
    var fmtRoutes = formatRoutes(resp.date)
    router.addRoutes(fmtRoutes)
    store.commit('initAdminMenu', fmtRoutes)
    })
  })
}

1.3.5 store文件夹:定义存储在Vuex 中的变量

index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
 
const store = new Vuex.Store({
 
  state: {
    // 存储token
    token: window.localStorage.getItem('token') ? window.localStorage.getItem('token') : '',
    // 存储username
    username: window.localStorage.getItem('username')  ? window.localStorage.getItem('username') : '',
    // 存储用户ID
    uid: -1,
    // 存储用户菜单
    adminMenus: []
  },
 
  mutations: {
    // 修改token,并将token存入localStorage
    setToken (state, user) {
      console.log(user)
      state.token = user;
      window.localStorage.setItem('token', user);
    },
    setUserName (state, user) {
      console.log(user)
      state.username = user;
      window.localStorage.setItem('username', user);
    },
    setUserId (state, user) {
      console.log(user)
      state.uid = user
    },
    initAdminMenu (state, menu) {
      state.adminMenus = menu
    },
    logout (state, user) {
      state.username = ''
      state.token = ''
      state.uid = -1
      state.adminMenus = []
      localStorage.removeItem('token')
      localStorage.removeItem('username')
    }
  }
});
 
export default store;

1.3.6 route文件夹:定义组件路由访问和路由守护

index.js

import Vue from 'vue'
import Router from 'vue-router'
import login from '@/components/login';
import Admin from '@/components/admin/AdminIndex'
import initAdminMenu from '../utils/initAdmin'
import store from  '../store'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      redirect: '/login'
    },
    {
      path: '/login',
      name: 'login',
      component: login
    },
    {
      path: '/admin',
      name: 'admin',
      component: Admin,
      redirect: '/admin/dashboard',
      children: [
        {
          path: '/admin/dashboard',
          name: 'adminDashboard',
          component: () => import('@/components/admin/dashboard/Index')
        },
        {
          path: '/admin/user/Profile',
          name: 'adminUserProfile',
          component: () => import('@/components/admin/user/Profile')
        },
        {
          path: '/admin/user/Role',
          name: 'adminUserRole',
          component: () => import('@/components/admin/user/Role')
        },
        {
          path: '/admin/test/Test1',
          name: 'adminTest1',
          component: () => import('@/components/admin/test/Test1')
        },
        {
          path: '/admin/test/Test2',
          name: 'adminTest2',
          component: () => import('@/components/admin/test/Test2')
        }
      ]
    }
  ]
})
// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from, next) => {
  if (to.path === '/login') {
    next();
  } else {
    let token = localStorage.getItem('token');
    if (token === null || token === '') {
      next('/login');
    } else {
      console.log('12345')
      // 查询动态表单
      initAdminMenu(router, store)
      // 跳转至指定页面
      next();
    }
  }
});
 
export default router;

1.3.7 components文件夹:定义组件

components/login.vue 定义:登入首页


<template>
  <div class="login-container">
    <el-form label-position="left"
      label-width="0px"
      status-icon>
        <h3 class="login_title">系统登录</h3>
        <el-form-item>
          <el-input
            type="text"
            v-model="loginForm.username"
            auto-coplete="off"
            placeholder="账号">
          </el-input>
        </el-form-item>
        <el-form-item>
          <el-input
            type="password"
            v-model="loginForm.password"
            auto-coplete="off"
            placeholder="密码">
          </el-input>
        </el-form-item>
        <el-form-item style="width: 100%">
          <el-button type="primary" @click.native.prevent="login" style="width: 100%">登录</el-button>
          <!--
          <el-button type="primary" @click.native.prevent="register" style="width: 100%; margin-top: 30px">注册</el-button>
          -->
        </el-form-item>
      </el-form>
  </div>
</template>
 
<script>
import { post, waitsleep } from '@/utils/http'
export default {
  data () {
    return {
      loginForm: {
        username: '',
        password: ''
      },
      userToken: ''
    };
  },
 
  methods: {
    login () {
      let _this = this;
      if (this.loginForm.username === '' || this.loginForm.password === '') {
        alert('账号或密码不能为空');
      } else {
        var params = {
        username: _this.loginForm.username,
        password: _this.loginForm.password
      }
        post('/common/login',params).then(res => {
           console.log(res);
          _this.userToken = res.token;
          // 将用户token保存到vuex中
          _this.$store.commit('setToken', res.token);
          _this.$store.commit('setUserName', res.username);
          _this.$store.commit('setUserId', res.uid);

          _this.$router.push('/admin');
          alert('登陆成功');
        })
        
      }
    }
  }
};
</script>
<style scoped>
.login-container{
  border-radius: 15px;
  background-clip: padding-box;
  margin: 10% auto;
  width: 350px;
  padding: 35px 35px 15px 35px;
  background: #fff;
  border: 1px solid #eaeaea;
  box-shadow: 0 0 25px #cac6c6;
}
.login_title{
  margin: 0px auto 40px auto;
  text-align: center;
  color: #505458;
}
</style>

components/home/Head.vue 定义:公共顶部栏组件

<!--  -->
<template>
  <div>
    <div class="home_title">后台系统</div>
    <div class="home_userinfoContainer">
      <el-dropdown @command="handleCommand">
        <span class="el-dropdown-link home_userinfo">
          {{currentUserName}}
          <i class="el-icon-arrow-down el-icon--right home_userinfo"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item command="goIndex">返回主页</el-dropdown-item>
          <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script>
import {post} from '@/utils/http'
export default {
  data () {
    return {
      currentUserName: ''
    }
  },
  mounted: function getUser () {
    this.currentUserName = this.$store.state.username
  },
  methods: {
    handleCommand (command) {
      var _this = this
      if (command === 'logout') {
        this.$confirm('注销登录吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(function () {
          post('/sys/logout')
          _this.$store.commit('logout')
          _this.$router.replace({name: 'login'})
          _this.$message.success('退出成功')
        }, function () {
          // 取消
        })
      } else if (command === 'goIndex') {
        this.$router.push({name: 'index'})
      }
    }
  }
}
</script>
<style>
  .el-header {
    background-color: #20a0ff;
    color: #333;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .home_title {
    color: #fff;
    font-size: 22px;
    display: inline;
  }

  .home_userinfo {
    color: #fff;
    cursor: pointer;
  }

  .home_userinfoContainer {
    position: absolute;
    right: 0px;
    display: inline;
    margin-right: 20px;
  }
</style>

components/home/Side.vue 定义:公共动态菜单栏组件

<!--  -->
<template>
 
    <el-menu router default-active="/admin/dashboard" mode="vertical" :collapse="isCollapse"  style="height:100%">
      <template v-for="(item,i) in adminMenus">
        <el-submenu :key="i" :index="i + ''" style="text-align: left">
          <span slot="title">
              <i :class="item.iconCls" style="font-size:10px"></i>
            {{item.nameZh}}
          </span>
          <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
            <i :class="child.icon"></i>
            {{ child.nameZh }}
          </el-menu-item>
        </el-submenu>
      </template>
    </el-menu>
  
</template>

<script>
export default {
  data () {
    return {
      isCollapse: false
      // adminMenus: [
      //   {
      //     iconCls: 'el-icon-s-home',
      //     nameZh: '首页',
      //     children: [
      //       {
      //         icon: '',
      //         nameZh: '首页1-1',
      //         path: '/admin/user/Profile'
      //       }
      //     ]
      //   }
      // ]
    }
  },
  computed: {
    adminMenus () {
      return this.$store.state.adminMenus
    }
  }
}
</script>

components/admin/AdminIndex.vue: 系统首页框架

<!--  -->
<template>
  <div>
    <el-container>
      <el-header>
        <Head></Head>
      </el-header>
      <el-container ref="homePage">
        <el-aside :width="sideWidth">
          <Side></Side>
        </el-aside>
        <el-main>
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
import Side from '@/components/home/Side'
import Head from '@/components/home/Head'
export default {
  data () {
    return {
      clientHeight: '',
      sideWidth: '200px'
    }
  },
  mounted: function () {
    // 获取浏览器可视区域高度
    this.clientHeight = `${document.documentElement.clientHeight}`
    this.sideWidth = document.body.clientWidth * 0.15 + 'px'
    // document.body.clientWidth;
    // console.log(self.clientHeight);
    window.onresize = function temp () {
      this.clientHeight = `${document.documentElement.clientHeight}`
    }
  },
  watch: {
    // 如果 `clientHeight` 发生改变,这个函数就会运行
    clientHeight: function () {
      this.changeFixed(this.clientHeight)
    }
  },
  methods: {
    changeFixed (clientHeight) {
      // 动态修改样式
      // console.log(clientHeight);
      // console.log(this.$refs.homePage.$el.style.height);
      this.$refs.homePage.$el.style.height = clientHeight - 80 + 'px'
    }
  },
  components: {
    Head,
    Side
  }
}
</script>
<style>
</style>

components/admin/dashboard/index.vue: 系统首页详情

<template>
    <div>
      首页
    </div>
</template>

<script>
export default {
  name: 'Index',
  data () {
    return {}
  },
  computed: {},
  watch: {},
  methods: {}
}
</script>

<style scoped>

</style>

components/admin/test/Test1.vue: 测试页面一

<template>
    <div>
      测试1
    </div>
</template>

<script>
export default {
  name: 'Test1',
  data () {
    return {}
  },
  computed: {},
  watch: {},
  methods: {}
}
</script>

<style scoped>

</style>

components/admin/test/Test2.vue: 测试页面二

<template>
    <div>
      测试2
    </div>
</template>

<script>
export default {
  name: 'Test2',
  data () {
    return {}
  },
  computed: {},
  watch: {},
  methods: {}
}
</script>

<style scoped>

</style>

components/admin/user/Role.vue: 角色配置详情页面

<template>
    <div>
      角色配置
    </div>
</template>

<script>
export default {
  name: 'Role',
  data () {
    return {}
  },
  computed: {},
  watch: {},
  methods: {}
}
</script>

<style scoped>

</style>

components/admin/user/Profile.vue: 用户详情页面

<template>
    <div>
        用户信息
    </div>
</template>

<script>
export default {
  name: 'Profile',
  data () {
    return {}
  },
  computed: {},
  watch: {},
  methods: {}
}
</script>

<style scoped>

</style>

1.3.8 package.json:需要安装的第三方工具包

1.4 效果展示

 

前端git 项目地址:https://github.com/zhouzhiwengang/eight.git

后台git 项目地址:https://github.com/zhouzhiwengang/lease_sys.git

Logo

前往低代码交流专区

更多推荐