SpringBoot + Shiro + MyBatis整合案例

SpringBoot + Shiro + MyBatis案例

1、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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.3.1.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.examplegroupId>
    <artifactId>demoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>demoname>
    <description>Demo project for Spring Bootdescription>

    <properties>
        <java.version>1.8java.version>
    properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.3version>
        dependency>

        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-springartifactId>
            <version>1.5.3version>
        dependency>
        
        <dependency>
            <groupId>com.github.theborakompanionigroupId>
            <artifactId>thymeleaf-extras-shiroartifactId>
            <version>2.0.0version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>

application.yml:

spring:
  profiles:
    active: dev

application-dev.yml:

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username: root
    password: root

mybatis:
  #mapper.xml的具体位置
  mapper-locations: classpath:mapping/*Mapper.xml
  #定义此包下的实体类的别名
  type-aliases-package: com.example.demo.entity

项目结构图:

SpringBoot + Shiro + MyBatis整合案例_第1张图片

在entity包中编写 User 和 Role实体类(IDEA安装lombok插件,具体自己百度):

package com.example.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private List<Role> roleList = null;
}
package com.example.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
    private Integer id;
    private String rolename;
}

在mapper包下编写接口UserMapper:

package com.example.demo.mapper;

import com.example.demo.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper {
    //根据id查找用户
    User selectUserById(Integer id);
    //根据username查找用户
    User selectUserByName(String username);
}

在resources/mapping目录下新建UserMapping.xml:



<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <result column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <collection property="roleList" ofType="role">
            <result column="rid" property="id"/>
            <result column="rolename" property="rolename"/>
        collection>
    resultMap>
    
    <select id="selectUserById" parameterType="Integer" resultMap="userMap">
        select u.*,r.id as rid,r.rolename
        from user as u
        left join user_role as ur on u.id = ur.user_id
        left join role as r on r.id = ur.role_id
        where u.id = #{id}
    select>

    
    <select id="selectUserByName" parameterType="String" resultMap="userMap">
        select u.*, r.id as rid,r.rolename
        from user as u
        left join user_role as ur on u.id = ur.user_id
        left join role as r on r.id = ur.role_id
        where u.username = #{username}
    select>
mapper>

在service包和service.impl包下新建UserService接口和对应实现类UserServiceImpl:

package com.example.demo.service;

import com.example.demo.entity.User;

public interface UserService {
    //根据id查询用户
    User selectUserById(Integer id);
    //根据用户名查询用户
    User selectUserByName(String username);
}
package com.example.demo.service.impl;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public User selectUserById(Integer id) {
        return userMapper.selectUserById(id);
    }

    @Override
    public User selectUserByName(String username) {
        return userMapper.selectUserByName(username);
    }
}

在controller包下添加UserController类 和 errorController类:

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.Session;

@Controller
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/")
    public String index(){
        return "index";
    }
    //到登录页面
    @RequestMapping("/user/toLogin")
    public String toLogin(){
        return "/user/login";
    }
    //到添加页面
    @RequestMapping("/user/add")
    public String toAddUser(){
        return "/user/add";
    }
    //到更新页面
    @RequestMapping("/user/update")
    public String toUpdateUser(){
        return "/user/update";
    }
    //验证用户登录
    @RequestMapping("/checkLogin")
    public String checkLogin(String username, String password, Model model){
        System.out.println(username + ":" + password);
        if (username.equals("") || password.equals("")){
            model.addAttribute("msg","用户名和密码不能为空!");
            return "forward:/user/toLogin";
        }
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            model.addAttribute("username",username);
            return "forward:/";
        }catch (UnknownAccountException e){
            model.addAttribute("msg","用户名不存在!");
            return "forward:/user/toLogin";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","用户名或密码错误!");
            return "forward:/user/toLogin";
        }
    }
    //注销用户
    @RequestMapping("/logout")
    public String logout(){
        Subject currentSubject = SecurityUtils.getSubject();
        currentSubject.logout();
        return "redirect:/";
    }
}
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/error")
public class ErrorController {
    //跳转到权限不足提示页面
    @RequestMapping("/noAuth")
    public String toNoAuth(){
        return "/error/noauth";
    }
}

在shiro包中编写UserRealm:

package com.example.demo.shiro;

import com.example.demo.entity.Role;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

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

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑!");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //获取当前登录对象
        Subject subject = SecurityUtils.getSubject();
        User user = (User)subject.getPrincipal();
        List<String> permissionList = new ArrayList<>();
        for (Role role : user.getRoleList()){
            permissionList.add(role.getRolename());
        }
        System.out.println(String.valueOf(permissionList));
        simpleAuthorizationInfo.addStringPermissions(permissionList);
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑!");
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken;
        User user = userService.selectUserByName(usernamePasswordToken.getUsername());
        System.out.println(user.toString());
        //用户名是否存在
        if (!usernamePasswordToken.getUsername().equals(user.getUsername())){
            return null;
        }
        //判断密码,第一个参数是将当前user添加到principal中,以便上面授权的时候获取
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
}

在config包下添加ShiroConfig:

package com.example.demo.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.example.demo.shiro.UserRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    //创建ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        /**    添加Shiro内置过滤器
         *     Shiro内置过滤器,可以实现权限相关的拦截器
         *    常用的过滤器:
         *       anon: 无需认证(登录)可以访问
         *       authc: 必须认证才可以访问
         *       user: 如果使用rememberMe的功能可以直接访问
         *       perms: 该资源必须得到资源权限才可以访问
         *       role: 该资源必须得到角色权限才可以访问
         */
        Map<String, String> filermap = new LinkedHashMap<>();
        filermap.put("/", "anon");
        filermap.put("/user/add","perms[管理员]");
        filermap.put("/user/update","perms[游客]");
        filermap.put("/user/toLogin", "anon");
        filermap.put("/user/*", "authc");
        //设置登录链接
        shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
        //设置未经授权的访问自动跳转链接
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/noAuth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filermap);
        return shiroFilterFactoryBean;
    }

    //创建DefaultWebSecurityManager
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //创建Realm
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }

    //整合ShiroDialect:用来整合shiro thymeleaf
    @Bean
    public ShiroDialect getShirodialect(){
        return new ShiroDialect();
    }
}

在templates下,目录结构如图:

SpringBoot + Shiro + MyBatis整合案例_第2张图片

noauth.html:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>权限不足提示title>
head>
<body>
<h3>对不起,您的权限不足!h3>
<div>div>
<script>
    var div = document.querySelector('div');
    var timer = 5;    //提前先调用以下函数 防止刷新时页面出现空白
    skip();
    setInterval(skip, 1000);
    function skip(){
        if(timer == 0){
            location.href = '/';
        }
        div.innerHTML = '您将在' + timer + '钟之后跳转到首页';
        timer--;
    }
script>
body>
html>

login.html:


<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>登录title>
head>
<body>
<h3>登录h3>
<div th:if="${msg} != null">
    <label th:text="${msg}" style="color:red;"/>
div>

<form action="/checkLogin" method="post">
    用户名:<input type="text" name="username"/> <br/>
    密码:<input type="password" name="password"> <br/>
    <button type="submit">提交button>
form>
body>
html>

add.html:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加用户title>
head>
<body>
<h3>添加用户界面!h3>
body>
html>

update.html:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更改用户title>
head>
<body>
<h3>更改用户界面!h3>
body>
html>

index.html:


<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>首页title>
head>
<body>
<h3>这是首页!h3>
<div th:if="${username} != null">
    <label th:text="${username}">欢迎您,${username}label>
    <br/>
    <a href="/logout">注销a>
div>

<div th:if="${username} == null">
    <a href="/user/toLogin">登录a>
div>

<div shiro:hasPermission="管理员">
    <a href="/user/add">添加用户a>
div>
<div shiro:hasPermission="游客">
    <a href="/user/update">更新用户a>
div>
body>
html>

数据库:

数据库名称:springboot

3个表分别为user、role、user_role:


CREATE TABLE `user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8


CREATE TABLE `role` (
  `id` int(8) NOT NULL AUTO_INCREMENT,
  `rolename` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8


CREATE TABLE `user_role` (
  `id` int(8) NOT NULL AUTO_INCREMENT,
  `user_id` int(8) DEFAULT NULL,
  `role_id` int(8) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8

表与表之间的关系如图:
SpringBoot + Shiro + MyBatis整合案例_第3张图片

说明:其实此项目中只需要使用一个表就行,但为了复杂化数据库,故特意弄了3张表。user表与role表之间是多对多关系,将user_role作为一张中间表,实现了role与user_role 和 user 与 user_role的一对多关系。

表中随便加点数据:

在这里插入图片描述

至此,项目搭建完成,启动springBoot的启动类,访问:localhost:8080/

你可能感兴趣的:(java)