SpringBoot + Vue 动态加载菜单栏
第一步:SpirngBoot 项目1.1 建库脚本/*Navicat MySQL Data TransferSource Server: 192.168.1.73Source Server Type: MySQLSource Server Version : 80015Source Host: 192.168.1.73:3306Source Schema: auth_shrioTarget Se.
·
第一步: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
更多推荐
已为社区贡献24条内容
所有评论(0)