xx管理系统之JAVA后端

6 篇文章 0 订阅

项目介绍:

一个前后端分离的管理系统。前端使用vue框架,后端使用Java的springboot框架。

后台框架:SpringBoot+SpringSecurity+jwt+mybatis-plus+swagger2
环境:JDK1.8
软件: idea

前端框架:vue ,vue_cli,element
前端Vue教程:Vue之完整dome
软件: HBuilder x


首先是数据库表
sys_user,sys_role,sys_user_role

CREATE TABLE `sys_role` (
  `Id` char(32) NOT NULL,
  `roleName` varchar(16) NOT NULL,
  `detail` varchar(255) DEFAULT NULL COMMENT '描述',
  `delflag` tinyint(1) NOT NULL COMMENT '删除标识:1-删除  0-未删除',
  `createTime` datetime DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限表';

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'ROLE_ADMIN', null, '0', null);
INSERT INTO `sys_role` VALUES ('2', 'ROLE_USER', null, '0', null);

--分割线----------------------------
CREATE TABLE `sys_user` (
  `id` char(32) NOT NULL,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  `delflag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标识:1-删除  0-未删除',
  `createTime` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$dB4FzduqBNFKAhEgwEtRae8Bco/c0nXaQyFXivVxiZMoz4Kyxg9Wy', '0', null);
INSERT INTO `sys_user` VALUES ('2', 'user', '$2a$10$jcg0hO4iFFo.15MKZ0MFbuneOwbna7uGBRw0NYCyXkGHSYOSaWg9a\r\n', '0', null);
INSERT INTO `sys_user` VALUES ('3', '小明', '$2a$10$kRhBvIZXB9p7bHNuShjqUe0em.gIznIDhrbd.mFAaTmns3EknAsbK\r\n', '0', null);
INSERT INTO `sys_user` VALUES ('4', '小红', '$2a$10$kRhBvIZXB9p7bHNuShjqUe0em.gIznIDhrbd.mFAaTmns3EknAsbK\r\n', '0', null);
INSERT INTO `sys_user` VALUES ('5', '小梅', '$2a$10$kRhBvIZXB9p7bHNuShjqUe0em.gIznIDhrbd.mFAaTmns3EknAsbK\r\n', '0', null);
INSERT INTO `sys_user` VALUES ('6', '小刚', '$2a$10$kRhBvIZXB9p7bHNuShjqUe0em.gIznIDhrbd.mFAaTmns3EknAsbK\r\n', '0', null);
INSERT INTO `sys_user` VALUES ('7', '小智', '$2a$10$61K6xRiuNpB5rR/Z7syvb.upcy0nNb0nsM9Il07h5jyHUG7vnnjwK\r\n', '0', null);

--分割线----------------------------
CREATE TABLE `sys_user_role` (
  `user_id` char(32) NOT NULL,
  `role_id` char(32) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `fk_role_id` (`role_id`),
  CONSTRAINT `fk_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`Id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户-角色表';

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

这里admin,user的账号和密码是一样的(如:账号:admin 密码:admin)。其他密码都是123。

1.idea 新建SpringBoot项目

初始结构如下
在这里插入图片描述

导入pom.xml依赖配置

 <properties>
        <java.version>1.8</java.version>
        <spring.version>2.3.5.RELEASE</spring.version>
        <mybatis-plus.version>3.4.1</mybatis-plus.version>
        <mg.version>1.3.7</mg.version>
        <mysql.version>8.0.22</mysql.version>
        <swagger.version>2.5.0</swagger.version>
        <lombok.version>1.16.10</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--mybatis-plus自动代码生成-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <dependency>
            <groupId>nl.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.2.4</version>
        </dependency>



        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>

    </dependencies>

2.配置application.yml

 server:
  port: 8090

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/materiel?serverTimezone=Asia/Shanghai&allowMultiQueries=true&characterEncoding=utf-8
    password: root
    username: root
  devtools:
    restart:
      additional-paths: src/main/java
      enabled: true
  thymeleaf:
    cache: true
  #切面启用
  aop:
    proxy-target-class: true
    auto: true

mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
  mapper-locations: classpath:/mapper/*.xml

3.使用代码自动生成工具类

3.1 依赖包和配置文件

使用mybatis-plus自动代码生成需导入包

       <!--mybatis-plus自动代码生成-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>

创建类MybatisPlusGenerator

package com.jykj.materiel;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;

public class MybatisPlusGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 创建代码生成器对象
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(scanner("请输入你的项目路径") + "/src/main/java");
        gc.setAuthor("xiaoge");
        //生成之后是否打开资源管理器
        gc.setOpen(false);
        //重新生成时是否覆盖文件
        gc.setFileOverride(false);
        //%s 为占位符
        //mp生成service层代码,默认接口名称第一个字母是有I
        gc.setServiceName("%sService");
        //设置主键生成策略  自动增长
        gc.setIdType(IdType.AUTO);
        //设置Date的类型   只使用 java.util.date 代替
        gc.setDateType(DateType.ONLY_DATE);
        //开启实体属性 Swagger2 注解
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/materiel?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        //使用mysql数据库
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("请输入模块名"));
        pc.setParent("com.jykj");
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setMapper("mapper");
        pc.setEntity("entity");
        pc.setXml("dao");
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //设置哪些表需要自动生成
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));

        //实体类名称驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);

        //列名名称驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //使用简化getter和setter
        strategy.setEntityLombokModel(true);
        //设置controller的api风格  使用RestController
        strategy.setRestControllerStyle(true);
        //驼峰转连字符
        strategy.setControllerMappingHyphenStyle(true);
        //忽略表中生成实体类的前缀
        //strategy.setTablePrefix("tb_");
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

运行main方法使用
在这里插入图片描述

3.2 创建controller类

实现简单的与数据库交互

@Controller
public class LoginController {
   
        @Autowired
        private SysUserService sysUserService;

        @GetMapping("/test")
        @ResponseBody
        public String test(){
            HashMap<String,Object> map=new HashMap<>();
            map.put("code",200);
            map.put("data","成功");
            List<SysUser> sysUser=sysUserService.list();
            map.put("items",sysUser);
            String jsonString = JSONObject.toJSONString(map);
            return jsonString;
        }
 }

3.3 运行项目查看是否成功

访问http://localhost:8090/test
在这里插入图片描述

4.同一公共返回类

4.1 状态码

package com.jykj.materiel.response;

/**
 * @Author: NieChangan
 * @Description: 返回码定义
 * 规定:
 * #200表示成功
 * #1001~1999 区间表示参数错误
 * #2001~2999 区间表示用户错误
 * #3001~3999 区间表示接口异常
 * #后面对什么的操作自己在这里注明就行了
 */
public enum ResultCode implements CustomizeResultCode{
    /* 成功 */
    SUCCESS(200, "成功"),

    /* 默认失败 */
    COMMON_FAIL(999, "失败"),

    /* 参数错误:1000~1999 */
    PARAM_NOT_VALID(1001, "参数无效"),
    PARAM_IS_BLANK(1002, "参数为空"),
    PARAM_TYPE_ERROR(1003, "参数类型错误"),
    PARAM_NOT_COMPLETE(1004, "参数缺失"),

    /* 用户错误 */
    USER_NOT_LOGIN(2001, "用户未登录"),
    USER_ACCOUNT_EXPIRED(2002, "账号已过期"),
    USER_CREDENTIALS_ERROR(2003, "密码错误"),
    USER_CREDENTIALS_EXPIRED(2004, "密码过期"),
    USER_ACCOUNT_DISABLE(2005, "账号不可用"),
    USER_ACCOUNT_LOCKED(2006, "账号被锁定"),
    USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),
    USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),
    USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"),

    /*部门错误*/
    DEPARTMENT_NOT_EXIST(3007, "部门不存在"),
    DEPARTMENT_ALREADY_EXIST(3008, "部门已存在"),

    /* 业务错误 */
    NO_PERMISSION(3001, "没有权限"),

    /*运行时异常*/
    ARITHMETIC_EXCEPTION(9001,"算数异常");

    private Integer code;

    private String message;

    ResultCode(Integer code,String message){
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

4.2 统一返回结果

package com.jykj.materiel.response;

/**
 * @author NieChangan
 */
public interface CustomizeResultCode {

    /**
     * 获取错误状态码
     * @return 错误状态码
     */
    Integer getCode();

    /**
     * 获取错误信息
     * @return 错误信息
     */
    String getMessage();
}

package com.jykj.materiel.response;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

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

/**
 * 公共返回结果
 * @author NieChangan
 */
@Data
public class Result {

    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map<String,Object> data = new HashMap<>();

    /**
     * 构造方法私有化,里面的方法都是静态方法
     * 达到保护属性的作用
     */
    private Result(){

    }

    /**
     * 这里是使用链式编程
     */
    public static Result ok(){
        Result result = new Result();
        result.setSuccess(true);
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMessage(ResultCode.SUCCESS.getMessage());
        return result;
    }

    public static Result error(){
        Result result = new Result();
        result.setSuccess(false);
        result.setCode(ResultCode.COMMON_FAIL.getCode());
        result.setMessage(ResultCode.COMMON_FAIL.getMessage());
        return result;
    }

    /**
     * 自定义返回成功与否
     * @param success
     * @return
     */
    public Result success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public Result message(String message){
        this.setMessage(message);
        return this;
    }

    public Result code(Integer code){
        this.setCode(code);
        return this;
    }

    public Result data(String key,Object value){
        this.data.put(key,value);
        return this;
    }

    public Result data(Map<String,Object> map){
        this.setData(map);
        return this;
    }
}

4.3 测试返回数据

修改test测试方法

@Controller
public class LoginController {
    private Logger logger= LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private SysUserService sysUserService;
    
    @GetMapping("/test")
    @ResponseBody
    public Result test(){
        List<SysUser> sysUser=sysUserService.list();
        return Result.ok().data("user",sysUser);
    }
}

访问咯擦

5.整合SpringSecurity

Springboot+SpringSecurity入门教程 https://blog.csdn.net/yuanlaijike/article/details/80249235

5.1.配置SpringSecurity

需要依赖包

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.3.5.RELEASE</version>
        </dependency>

UserDetailsServiceImpl类:用于获取用户和分配权限。

package com.jykj.materiel.config.securityConfig;

import com.jykj.materiel.system.entity.SysRole;
import com.jykj.materiel.system.entity.SysUser;
import com.jykj.materiel.system.entity.SysUserRole;
import com.jykj.materiel.system.service.SysRoleService;
import com.jykj.materiel.system.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Author: hjl
 * @Date: 2020/11/10 0010 15:27
 */
@Service("UserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService userService;

    @Autowired
    private SysRoleService roleService;

    @Autowired
    private SysUserService.SysUserRoleService userRoleService;
    @Override
    public UserDetails loadUserByUsername(String username) {
        System.out.println("userName:"+ username);
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        // 从数据库中取出用户信息
        SysUser user = userService.selectByName(username);

        // 判断用户是否存在
        if(user == null) {
            throw new BadCredentialsException("用户名不存在");
        }

        // 添加权限
        List<SysUserRole> userRoles = userRoleService.listByUserId(user.getId());
        for (SysUserRole userRole : userRoles) {
            SysRole role = roleService.selectById(userRole.getRoleId());
            authorities.add(new SimpleGrantedAuthority(role.getRolename()));
        }

        // 返回UserDetails实现类
        return new User(user.getName(), user.getPassword(), authorities);
    }
}

WebSecurityConfig 类:配置拦截、跳转和比较密码

package com.jykj.materiel.config.securityConfig;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


/**
 * @Author: hjl
 * @Date: 2020/11/10 0010 15:31
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            /**
             * @param charSequence 输入的密码
             * @param s 数据库加密的密码
             * @return
             */
            @Override
            public boolean matches(CharSequence charSequence, String s) {
                //System.out.println("matches:-》charSequence: "+charSequence.toString()+" ,S: "+s);
                BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
                boolean f = bcryptPasswordEncoder.matches(charSequence,s);//比较密码
                System.out.println("比较密码:"+f);
                return f;
            }
        });


    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                // 如果有允许匿名的url,填在下面
                .antMatchers("/regis","/register","/test").permitAll()
                .anyRequest().authenticated()
                .and()
                // 设置登陆页
                .formLogin().loginPage("/login")
                // 设置登陆成功页
                .defaultSuccessUrl("/").permitAll()
                // 自定义登陆用户名和密码参数,默认为username和password
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                // 异常处理跳转
                .exceptionHandling().accessDeniedPage("/500")
                .and()
                .logout().permitAll();
        //开启记住我功能
        // http.rememberMe().rememberMeParameter("remember-me");

        //以下这句就可以控制单个用户只能创建一个session,也就只能在服务器登录一次
        http.sessionManagement().maximumSessions(1).expiredUrl("/login");

         // 关闭CSRF跨域
        http.csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置拦截忽略文件夹,可以对静态资源放行
        web.ignoring().antMatchers("/css/**", "/js/**");
    }


}


5.2登录注册业务

在template目录下新建 login.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>登陆1</h1>
<form method="post" action="">
    <div>
        用户名:<input type="text" name="username">
    </div>
    <div>
        密码:<input type="password" name="password">
    </div>
    <!--<div class="checkbox">-->
        <!--<label><input type="checkbox" id="rememberme" name="remember-me"/>记住我</label>-->
    <!--</div>-->

    <div>
        <button type="submit">立即登陆</button>
    </div>
    <a href="/regis"> 注册</a>
</form>

</body>
</html>

在刚刚的配置类WebSecurityConfig 中,拦截跳转。
在这里插入图片描述
在loginController中添加登录页和登录成功页面跳转。

	@GetMapping("/login")
    public String showLogin() {
        return "login";
    }
    
    @GetMapping("/")
    public String showHome(){
        String name= SecurityContextHolder.getContext().getAuthentication().getName();
        System.out.println("当前登录用户:"+ name);
        return "home.html";
    }

由于在WebSecurityConfig中配置了登录页和成功跳转页,所以这里不用写action的值。
访问首页http://localhost:8090/会被拦截跳转到自定义的login页面。
在这里插入图片描述
在这里插入图片描述
控制台打印顺序
在这里插入图片描述

根据注解识别权限
在loginController中添加ROLE_ADMIN和ROLE_USER权限控制

   @GetMapping("/admin")
    @ResponseBody
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String printAdmin() {
        return "如果你看见这句话,说明你有ROLE_ADMIN角色";
    }

    @GetMapping("/user")
    @ResponseBody
    @PreAuthorize("hasRole('ROLE_USER')")
    public String printUser() {
        return "如果你看见这句话,说明你有ROLE_USER角色";
    }

你可以自己测试一下,注意数据库中用户是否有权限。
注册时的加密使用的是BCryptPasswordEncoder 。

 	@Test
    void contextLoads() {
       BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
       String encode = encoder.encode("123");
       System.out.println(encode);
       String charSequence="$2a$10$61K6xRiuNpB5rR/Z7syvb.upcy0nNb0nsM9Il07h5jyHUG7vnnjwK";
       boolean f = bcryptPasswordEncoder.matches(charSequence,s);//比较密码
       System.out.println("比较密码:"+f);
    }

6.整合swagger2

6.1配置swagger2

package com.jykj.materiel.config;

import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @Author: hjl
 * @Date: 2020/12/4 0004 14:02
 */
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //只扫描有注解的方法并显示
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("xx管理系统API文档")
                .description("接口 http://localhost:8090/")
                .termsOfServiceUrl("www.xx.cn")
//                .contact(new Contact("xx科技","www.xxkj.com","xxkj@198.com"))
                .version("1.0")
                .build();
    }
}

在对应方法上添加注解

@Controller
@Api(description = "登录相关api")
public class LoginController {
    private Logger logger= LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private SysUserService sysUserService;
    
    @GetMapping("/test")
    @ResponseBody
    @ApiOperation(value="测试接口",notes = "测试查询所有用户信息")
    public Result test(){
        List<SysUser> sysUser=sysUserService.list();
        return Result.ok().data("user",sysUser);
    }
}
      

重新启动项目 访问: http://localhost:8090/swagger-ui.html

在这里插入图片描述

6.2使用knife4j美化swagger2

需要依赖包knife4j
官方地址:https://xiaoym.gitee.io/knife4j/documentation/get_start.html

		<dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.7</version>
        </dependency>

由于使用swagger2 已经配置好了apiInfo()这里就没有重新配置。
访问地址:http://localhost:8090/doc.html
在这里插入图片描述

7.同一异常处理

新建GlobalExceptionHandler.class类

package com.jykj.materiel.handler;

import com.jykj.materiel.response.Result;
import com.jykj.materiel.response.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Author: hjl
 * @Date: 2020/12/7 0007 13:24
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     *
     * 全局异常处理,捕获所有异常。
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
//        e.printStackTrace();
        log.error(e.getMessage());
        return Result.error();
    }

	/**
     * 自定义ArithmeticException的异常处理,捕获并返回。
     * @param e
     * @return
     */
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public Result error(ArithmeticException e){
//        e.printStackTrace();
        log.error(ResultCode.ARITHMETIC_EXCEPTION.getMessage());
        return Result.error()
                .code(ResultCode.ARITHMETIC_EXCEPTION.getCode())
                .message(ResultCode.ARITHMETIC_EXCEPTION.getMessage());
    }
}

在LoginController中添加异常

	@GetMapping("/test")
    @ResponseBody
    @ApiOperation(value="测试接口",notes = "测试查询所有用户信息")
    public Result test(){
        int i=1/0; //测试捕获算数异常ArithmeticException
        List<SysUser> sysUser=sysUserMapper.list();
        return Result.ok().data("user",sysUser);
    }

在这里插入图片描述

8.同一日志处理

配置application.yml 添加Slf4jImpl

mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

配置logback-spring.xml
只需修改日志输出路径和sql打印位置

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

<!-- 级别从高到低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 -->
<!-- scan 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 -->
<!-- scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!-- 动态日志级别 -->
    <jmxConfigurator/>

    <!--*****************************************************************************-->
    <!--自定义项 开始-->
    <!--*****************************************************************************-->

    <!-- 定义日志文件 输出位置 -->
    <property name="log.home_dir" value="D:/dome/Materiel-logs"/>
    <property name="log.app_name" value="http-demo"/>
    <!-- 日志最大的历史 30天 -->
    <property name="log.maxHistory" value="30"/>
    <property name="log.maxSize" value="5MB"/>
    <!-- 日志界别 -->
    <property name="log.level" value="info"/>
    <!-- 打印sql语句 需要指定dao/mapper层包的位置 -->
    <property name="mapper.package" value="com.jykj.materiel.system.dao"/> 

    <!-- 彩色日志 -->
    <!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
    <!-- magenta:洋红 -->
    <!-- boldMagenta:粗红-->
    <!-- cyan:青色 -->
    <!-- white:白色 -->
    <!-- magenta:洋红 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>

    <!--*****************************************************************************-->
    <!--自定义项 结束-->
    <!--*****************************************************************************-->

    <!-- ConsoleAppender 控制台输出日志 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                ${CONSOLE_LOG_PATTERN}
            </pattern>
        </encoder>
    </appender>

    <!-- ERROR级别日志 -->
    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
    <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录WARN级别的日志 -->
        <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>ERROR</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--日志输出位置 可相对、和绝对路径 -->
            <fileNamePattern>
                ${log.home_dir}/error/%d{yyyy-MM-dd}/${log.app_name}-%i.log
            </fileNamePattern>
            <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,且<maxHistory>是6,
            则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 -->
            <maxHistory>${log.maxHistory}</maxHistory>
            <!--日志文件最大的大小-->
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
    </appender>


    <!-- WARN级别日志 appender -->
    <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录WARN级别的日志 -->
        <!-- 果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置过滤级别 -->
            <level>WARN</level>
            <!-- 用于配置符合过滤条件的操作 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--日志输出位置 可相对、和绝对路径 -->
            <fileNamePattern>${log.home_dir}/warn/%d{yyyy-MM-dd}/${log.app_name}-%i.log</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <!--当天的日志大小 超过MaxFileSize时,压缩日志并保存-->
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>


    <!-- INFO级别日志 appender -->
    <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.home_dir}/info/%d{yyyy-MM-dd}/${log.app_name}-%i.log</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n</pattern>
        </encoder>
    </appender>


    <!-- DEBUG级别日志 appender -->
    <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.home_dir}/debug/%d{yyyy-MM-dd}/${log.app_name}-%i.log</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n</pattern>
        </encoder>
    </appender>


    <!-- TRACE级别日志 appender -->
    <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.home_dir}/trace/%d{yyyy-MM-dd}/${log.app_name}-%i.log</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n</pattern>
        </encoder>
    </appender>


    <!--设置一个向上传递的appender,所有级别的日志都会输出-->
    <appender name="app" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.home_dir}/app/%d{yyyy-MM-dd}/${log.app_name}-%i.log</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <MaxFileSize>${log.maxSize}</MaxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n</pattern>
        </encoder>
    </appender>


    <!--org.springframework.web包下的类的日志输出-->
    <logger name="org.springframework.web" additivity="false" level="WARN">
        <appender-ref ref="WARN"/>
    </logger>
    <!--dao层包下的类的日志输出-->
    <logger name="${mapper.package}" additivity="false" level="DEBUG">
        <appender-ref ref="app"/>
        <appender-ref ref="ERROR"/>
        <!--打印控制台-->
        <appender-ref ref="CONSOLE"/>
    </logger>


    <!-- root级别   DEBUG -->
    <root>
        <!-- 打印debug级别日志及以上级别日志 -->
        <level value="${log.level}"/>
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
        <!-- 不管什么包下的日志都输出文件 -->
        <appender-ref ref="ERROR"/>
        <appender-ref ref="INFO"/>
        <appender-ref ref="WARN"/>
        <appender-ref ref="DEBUG"/>
        <appender-ref ref="TRACE"/>
    </root>

</configuration>

在这里插入图片描述

9.分页查询

使用mybatis-plus的Page进行分页。
官网:https://baomidou.com/
pom.xml引入依赖

		<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

9.1 mybatis-plus单表分页

package com.jykj.materiel.system.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jykj.materiel.response.Result;
import com.jykj.materiel.system.entity.SysUser;
import com.jykj.materiel.system.service.SysUserService;
import io.swagger.annotations.ApiOperation;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Author: hjl
 * @Date: 2020/12/7 0007 15:12
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private SysUserService  sysUserService;

	// mybatis单表分页很简单使用page
    @PostMapping("/findUserList")
    @ApiOperation(value="测试分页",notes = "测试查询所有用户数据")
    public Result  findUserList(@Param("current")long current,@Param("size")long size) {
        // 对用户进行分页,泛型中注入实体类
        Page<SysUser> page=new Page<>(current,size);
        Page<SysUser> userPage=sysUserService.page(page);
        long total=userPage.getTotal();
        // 使用Math.ceil()向上取整 如果使用的vue框架不用计算页数
        //int PageCount=(int) Math.ceil((double) total/(double)size);
        List<SysUser> records=userPage.getRecords();
        return Result.ok().data("total",total)
        				//.data("PageCount",PageCount)
        				.data("records",records);
    }
}

测试结果:
在这里插入图片描述

9.2 mybatis-plus多表条件查询分页

使用 mybatis-plus的IPage
首先查看IService中源码,会发现有一个page翻页查询
在这里插入图片描述
和之前的单表查询一样传入page,只是多了个实体对象封装操作类 queryWrapper 。

这里以用户表和权限角色表联合条件查询为例;
给其他用户赋予 ROLE_USER 角色:

INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('2', '2');
INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('3', '2');
INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('4', '2');
INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('5', '2');
INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('6', '2');
INSERT INTO `materiel`.`sys_user_role` (`user_id`, `role_id`) VALUES ('7', '2');

sys_user 、sys_user_role 、sys_role 多表联合查询

SELECT
	u.id Id,u.name name,u.`password` password,s.roleName roleName
FROM
	sys_user AS u
LEFT JOIN sys_user_role AS r ON u.id = r.user_id
LEFT JOIN sys_role as s on s.Id=r.role_id

在这里插入图片描述

多表条件分页:
①.SysUserService中:

public interface SysUserService extends IService<SysUser> {
 IPage<SysUser> UserPage(Page<SysUser> page, @Param(Constants.WRAPPER) QueryWrapper<SysUser> wrapper);
}

②.SysUserServiceImpl中:

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
  
	@Override
    public IPage<SysUser> UserPage(Page<SysUser> page, QueryWrapper<SysUser> wrapper) {
        return this.baseMapper.UserPages(page,wrapper);
    }
 }

③.SysUserMapper中:

@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
 
 IPage<SysUser> UserPages(Page<SysUser> page, @Param(Constants.WRAPPER) QueryWrapper<SysUser> queryWrapper);
}

④.SysUserMapper.xml中:
${ew.customSqlSegment} 是传入的条件语句
如果有重复数据这里必须使用GROUP BY ,因为要查询记录条数。

<select id="UserPages" resultType="com.jykj.materiel.system.entity.SysUser">
        SELECT
	u.id Id,u.name name,u.`password` password,s.roleName roleName
FROM
	sys_user AS u
LEFT JOIN sys_user_role AS r ON u.id = r.user_id
LEFT JOIN sys_role as s on s.Id=r.role_id
 ${ew.customSqlSegment}
 GROUP BY Id 
    </select>

⑤.新建类实体类SysUserVo查询条件类:

@Data
public class SysUserVo {
   private String Name;

   private String roleName;
}

⑥.SysUserController中调用:
关于注解@RequestBody :https://www.cnblogs.com/zly123/p/10853049.html

    @PostMapping("/finds")
    @ApiOperation(value="多表条件分页",notes = "测试条件查询多表所有用户数据")
    public Result  finds(@RequestBody SysUserVo userVo , @RequestParam("current")long current, @RequestParam("size")long size) {
        // 对用户进行分页,泛型中注入实体类
        Page<SysUser> page=new Page<>(current,size);
        QueryWrapper<SysUser> queryWrapper = getUserWrapper(userVo);
        IPage<SysUser> userPage=sysUserService.UserPage(page,queryWrapper);
        long total=userPage.getTotal();
        List<SysUser> users=userPage.getRecords();
        return Result.ok().data("total",total).data("users",users);
    }

    private QueryWrapper<SysUser> getUserWrapper(SysUserVo userVo){
        QueryWrapper<SysUser> queryWrapper=new QueryWrapper<>();
        if (userVo!=null){
            if (!StringUtils.isEmpty(userVo.getName())){
                queryWrapper.eq("name",userVo.getName());
            }
            if (!StringUtils.isEmpty(userVo.getRoleName())){
                queryWrapper.eq("roleName",userVo.getRoleName());
            }
        }
        return queryWrapper;
    }

分页
在这里插入图片描述
在这里插入图片描述
输入条件分页查询:
在这里插入图片描述
在这里插入图片描述

10.整合SpringSecurity和Jwt

可以看这篇博客:SpringSecurity权限管理系统实战—六、SpringSecurity整合JWT

参考:SpringSecurity整合JWT

11.业务逻辑

后台github地址:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值