springboot+mybatis+thymeleaf学习一个简单的管理系统

发布时间:2022-10-08 14:00

在淘宝上买的课程的一个例子,看了视频,抄了一遍代码,那时候刚开始学springboot,所以感觉没什么用,然后就又学习了一段时间。最近回想起来有这样的一个系统符合我现阶段的学习程度,然后就又写了一遍。其中css和js实在没法自己搞,所以就直接把课程里的拿来用。所有做完后的样式确实不好看,但我自己觉得看的过去。
今天下午可以算是完工了(也就是增删改查),又想起大佬的话,也怕自己忘了,所以写博客记录一下。

1.技术方法

springboot+thymeleaf+mybatis+shiro

1.1 pom文件



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.4.RELEASE
         
    
    com.example
    demo
    0.0.1-SNAPSHOT
    bill
    Demo project for Spring Boot

    
        1.8
    

    
        
            com.alibaba
            druid
            1.1.13
        
        
            org.springframework.boot
            spring-boot-starter-data-jdbc
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            mysql
            mysql-connector-java
            runtime
        
        
        
            org.apache.shiro
            shiro-spring
            1.4.1
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.1
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



pom依赖差不多用的最新的版本,自己学习用的,应该没什么大的影响。

1.2 springboot使用thymeleaf

springboot使用thymeleaf我觉的比较方便,在HTML中引入,就能使用thymeleaf的功能(不知道这样说对不对)。thymeleaf特性的使用还是要多学习的,能够灵活应用的感觉应该非常不错。

1.3 springboot-mybatis

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录(百度)。
刚接触springboot的时候就是学的mybatis,所以可能情有独钟吧。

1.4 springboot-shiro

本来的教程上没有这个技术点,但我在学习的过程中接触到了shiro,有一篇springboot+Vue的教学博客让我记忆犹新,所以我决定试一下。在这里给大家推荐一下 Evan-Nightly大佬的这篇博客 Vue + Spring Boot 项目实战,也给自己插个眼。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序(百度)。

2.代码梳理

2.1 结构

springboot+mybatis+thymeleaf学习一个简单的管理系统_第1张图片
java类大概就是这些。
WebMvcConfig.java是拦截器、过滤器和视图控制器的配置文件。

package com.example.config;

import com.example.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {
    //添加视图控制器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    //main.html是显示在浏览器网址上的,main/main是项目main下面的main/html
        registry.addViewController("main.html").setViewName("main/main");
        registry.addViewController("index.html").setViewName("main/index");
    }
    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
        //拦截所有请求
                .addPathPatterns("/**")
                //不拦截
                .excludePathPatterns("/", "/login", "/index.html")
                //测试用的请,不拦截,可在项目上线时删掉
                .excludePathPatterns("/test", "/thymeleaf");
    }
}

我在刚开始学springboot的时候,拦截器、过滤器和视图控制器还挺难理解的,虽然看了几遍概念介绍但感觉还是无法理解这到底是个什么东西。在做这个项目的时候我自己多试了几遍。改一个编一次看一下什么情况,慢慢的就知道是什么作用了。自学,笨鸟先飞,是这样的,没办法。
ShiroConfig.java是shiro配置类

package com.example.config;

import com.example.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Create by Administrator on 2020/2/24.
 */
@Configuration
public class ShiroConfig {

    @Bean
    public static  LifecycleBeanPostProcessor getLifecycleBeanProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getMyRealm());
        return securityManager;
    }
    @Bean
    public MyRealm getMyRealm() {
        MyRealm wjRealm = new MyRealm();
        wjRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return wjRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

shiro的配置具体是什么意思我还真说不出来,感觉自己也就知道的大概,知其然而不知所以然。
我只用到了shiro的用户信息加密和认证登录功能,就是给用户注册时的密码加密,然后在登录时再比对加密的密码。
MyRealm.java

package com.example.realm;

/**
 * Create by Administrator on 2020/2/24.
 */
public class MyRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;
    //简单重写获取授权信息方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        return s;
    }

    //获取认证信息,即根据token中的用户名从数据库中获取密码和盐等信息并返回
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String userName = authenticationToken.getPrincipal().toString();
        User user = userService.getUserByName(userName);
        String passwordInDB = user.getPassword();
        String salt = user.getSalt();
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt), getName());
        return authenticationInfo;
    }
}

MyRealm和ShiroConfig.java配合使用,其中的奥秘我还没懂,希望有懂的大佬帮忙教一下。

2.2 登录模块

package com.example.controller;

@Controller
public class LoginController {

    @Autowired
    UserService userService;
//跳转到用户登录界面
    @RequestMapping("/index")
    public String index(){
        return "main/index";
    }
    //在页面点击登录按钮后执行登录
    @RequestMapping("/login")
    //username和password是前端传递过来的,这就是前后传递的一种方式
    public String login(HttpSession httpSession, String username, String password){
        System.out.println("username="+username+",password="+password);
        //判断是否为空,这个可以改到前端独立判断
        if(username.equals("") || password.equals("")){
        //如果为空就跳转到登录页
            return "redirect:/index";
        }
        //根据用户名获取用户
        User loginuser = userService.getUserByName(username);
        if(loginuser == null){
            System.out.println("user==null");
            return "redirect:/index";
        }
        //简单的判断密码是否正确
//        if(!loginuser.getPassword().equals(password)){
//            System.out.println("error password");
//            return "redirect:/index";
//        }
        //shiro获取subject
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        try {
            subject.login(usernamePasswordToken);
            //存储到session中,拦截器中判断是否登录
            httpSession.setAttribute("loginUser", loginuser);
            return "redirect:/main.html";
        }catch (AuthenticationException e){
            return "redirect:/index";
        }
//        httpSession.setAttribute("loginUser", loginuser);
//        return "redirect:/main.html";
    }
//跳转到主页面
    @GetMapping("/main/main")
    public String mainMain(HttpServletRequest httpServletRequest){
        System.out.println("mainMain method="+httpServletRequest.getMethod());
        return "redirect:/main.html";
    }
    //退出登录
    @GetMapping("/logout")
    //public String logout(HttpSession httpSession){
    public String logout(HttpServletRequest httpServletRequest){
        System.out.println("logout method="+httpServletRequest.getMethod());
        Subject subject = SecurityUtils.getSubject();
        subject.logout();

        //httpSession.removeAttribute("loginUser");
        //httpSession.invalidate();
        return "redirect:/index";
    }
}

我不知道怎么讲,所以把该说的都用注释写出来
index.html





    
    login
    
    


提交后,后台判断用户是否存在,用户的密码是否正确,然后跳转到主界面main.html,然后点击菜单就会跳转到相应的界面。
springboot+mybatis+thymeleaf学习一个简单的管理系统_第2张图片

2.3 用户模块

登录之后自然会有一些操作,但大多都基于对数据库的增删改查。

User.java

package com.example.bean;

import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;
/**
 * Create by Administrator on 2020/2/19.
 */
public class User {
    private Integer id;
    private String username;
    private String realName;
    private String password;
    private String salt;
    //性别 1:女   2:男
    private int gender;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    //1管理员 2经理 3普通用户
    private int userType;

    public User(Integer id, String username, String realName, String password, String salt, int gender, Date birthday, int userType) {
        this.id = id;
        this.username = username;
        this.realName = realName;
        this.password = password;
        this.salt = salt;
        this.gender = gender;
        this.birthday = birthday;
        this.userType = userType;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", realName='" + realName + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                ", gender=" + gender +
                ", birthday=" + birthday +
                ", userType=" + userType +
                '}';
    }
    
getter() and setter()...

在创建user类时要注意id的类型最好用Integer,否则在增加用户时会有问题,应为id在数据库中是自增的,而在增加用户时页面上没有,会有问题。
还有在建user表时password和salt的大小要大一点,我当时用25个字节结果加了shiro加密后有报错提示(具体的报错就不回放了)。
还有用户的生日也会有问题,要注意加上@DateTimeFormat(pattern = “yyyy-MM-dd”)注解。如果不加从页面保存时(增加或者更新)会有类型转换的错误。
然后重点说一下和shiro有关的内容,就是在注册时密码加密的过程。

@PostMapping("/user/add")
    public String add(HttpServletRequest httpServletRequest, User user){
        System.out.println("add method="+httpServletRequest.getMethod());
        String password = user.getPassword();
        String username = user.getUsername();
        username = HtmlUtils.htmlEscape(username);
        user.setUsername(username);
        User user1 = userService.getUserByName(username);
        if(null != user1){
            return "redirect:/list/users";
        }
        //生成盐,默认长度16位
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        //设置hash迭代次数
        int times = 2;
        //获取hash后的密码
        String encodingPassword = new SimpleHash("md5", password, salt, times).toString();
        user.setSalt(salt);
        user.setPassword(encodingPassword);
        userService.addUser(user);
        return "redirect:/list/users";
    }

大概都写到注释里了。
再说一下在页面上展示所有用户,list.html


            
        
用户名 真实姓名 性别 出生日期 用户类型 操作
xxx xx x xxx xx 查看 修改 删除

后台从数据库中查找出所有用户传递给前端

//列出所有用户
    @RequestMapping("/list/users")
    public String listUser(Map map){

        List users = userService.uListAll();
        map.put("users", users);
        return "user/list";
    }

修改用户页update.html

*
*
管理员 经理 普通用户

在修改页需要显示用户原本的信息,所以前后端需要用id来绑定一个user。

//查看用户详细信息
    @GetMapping("/user/{id}")
    //参数type来区分前端是要查看用户详细信息还是要修改用户信息
    public String view(HttpServletRequest httpServletRequest, @PathVariable("id") int id,
                       @RequestParam(value = "type", defaultValue = "view") String type,
                       Map map){
        System.out.println("view method="+httpServletRequest.getMethod());
        //通过前端传来的id获取到用户
        User user = userService.getUserById(id);
        //返回给前端用于页面显示用户原本的信息
        map.put("user", user);
        return "user/"+type;
    }

点击保存按钮好提交修改的用户信息

//更新用户信息
    @PostMapping("/user")
    public String update(HttpServletRequest httpServletRequest, User user){
        System.out.println("update method="+httpServletRequest.getMethod());
        userService.updateUser(user);
        return "redirect:/list/users";
    }

删除我就简单做了一下点击删除按钮直接从数据库删除

删除
//删除用户
    @GetMapping("/delete/user/{id}")
    public String delete(HttpServletRequest httpServletRequest, @PathVariable(value = "id") int id){
        System.out.println("delete method="+httpServletRequest.getMethod());
        userService.deleteUserById(id);
        return "redirect:/list/users";
    }

3. 总结

现在就总结好像有点早,但我写着写着发现好像没什么东西能写,或许可能功能比较少吧,Bill和Provider都和user类似,也是对数据库的增删改查操作。所以就不再赘述。
之后应该还会去完善一下,加上log系统和druid后台监控。
最后分享一下GitHub链接
https://github.com/azermu-milk/SpringbootTest.git
供大家clone。近期应该还会更新吧。

4.增加druid后台监控

4.1 介绍

在config文件夹下创建DruidConfig.java类,配置druid后台管理类和druid的filter类,当然还需要在application.yml中补全druid配置。
DruidConfig.java

package com.example.config;

/**
 * Create by Administrator on 2020/2/25.
 */
@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return new DruidDataSource();
    }

    //1.配置一个后台管理servlet
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");

        //设置初始化参数
        Map initParam = new HashMap<>();
        initParam.put(StatViewServlet.PARAM_NAME_USERNAME, "root");
        initParam.put(StatViewServlet.PARAM_NAME_PASSWORD, "root");
        //如果不写 则默认所有ip都能访问
        initParam.put(StatViewServlet.PARAM_NAME_ALLOW, "");
        //写入本地电脑的IP地址
        initParam.put(StatViewServlet.PARAM_NAME_DENY, "192.xxx.11.1");

        bean.setInitParameters(initParam);
        return bean;
    }

    //2.配置druid的filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());

        Map initParam = new HashMap<>();
        initParam.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.css,/druid/*");
        bean.setInitParameters(initParam);

        //设置拦截请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

大概就是这样,如注释所示。
application.yml配置

    #数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    #间隔多久进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    #配置有一个连接在连接池中的最小生存时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,slf4j
    maxPoolPreparedStatementPerConnectionSize: 25
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

4.2 后台监控展示

springboot+mybatis+thymeleaf学习一个简单的管理系统_第3张图片
springboot+mybatis+thymeleaf学习一个简单的管理系统_第4张图片
刚开始我在statViewServlet()上少写了个@Bean,结果怎么改都没有后台的登录界面,全被我的拦截器拦截了,不过经过我再三检查终于被我发现了这个问题,哈哈。

5.日志框架

看了半天日志框架什么也没看明白,就把自己想用到的加进来了。

logging:
  level:
    com.example.mapper: debug
  #path和name二选一,name的优先级会高于path
  file:
    path: C:\Users\Administrator\IdeaProjects\mybill\
    #name: BillLog.log
  #如果使用yml做配置文件,格式要用'',如果是properties则不用
  pattern:
    #console: '%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n'
    #把控制台的日志输出到文件的格式
    file: '%d{yyyy/MM/dd-HH:mm:ss} ===== [%thread] ===== %-5level ===== %logger ===== %msg%n'

这个改动也在GitHub上提交了。整个项目是完整的,拉下来加到IDEA中就能启动成功。

5.1分析日志底层实现

在web项目当中引用了 spring-boot-starter-web 依赖


  org.springframework.boot
  spring-boot-starter-web

spring-boot-starter-web 中引入了 spring-boot-starter 启动器

  org.springframework.boot
  spring-boot-starter
  2.0.6.RELEASE
  compile

spring-boot-starter 中引入了 spring-boot-starter-logging 日志启动器

  org.springframework.boot
  spring-boot-starter-logging

spring-boot-starter-logging 日志启动器 采用的是 logback 日志框架

  ch.qos.logback
  logback-classic
  1.2.3
  compile

SpringBoot中默认日志启动器为 spring-boot-starter-logging ,默认采用的是 logback日志框架

logback的默认配置

 
 
 
 
 
  
 
 
  
  
   
   
 

6.写在最后

这个项目到这里应该结束了,我不会再动它了。
又该去找新的项目学习去了。。。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号