1-1 shiro与springboot入门(springboot+mybatis+shiro+thymleaf)

shiro与springboot入门(springboot+mybatis+shiro+thymleaf)

项目结构
01-demo
    |--com.demo
        |--ShiroApplication(启动类)
        |--bean(java实体对象)
            |--Permission,Role,User
        |--config(配置文件)
            |--ShiroConfig
        |--controller(控制器)
            |--LoginController,UserController
        |--mapper(mapper接口)
            |--PermissionMapper,RoleMapper,UserMapper
        |--realm
            |--ShiroRealm
        |--service(Service类)
            |--UserService
    |--resource
        |--config(配置文件)
            |--application.properties,logback-spring.xml
        |--mapper.shiro(mapper文件)
            |--PermissionMapper,RoleMapper,UserMapper
        |--templages(页面文件)
            |--index.xml,login.xml,unauthorized.xml
        |--data.sql(数据库脚本)

一、项目搭建

1.1、创建基础项目,添加引用库

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
    </parent>
​
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
    </properties>
说明:
  • thymeleaf对应的版本是thymeleaf目前需要添加的版本

1.2、当前项目添加引用

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>
        <!-- Spring Boot 整合Mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- MySql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <!-- 整合shiro框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro-thymeleaf 2.0.0-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
​
    </dependencies>

1.3、配置文件:application.properties

#配置tomcat
server.port=2001
server.servlet-path=/
​
#关闭默认模板引擎缓存
spring.thymeleaf.cache=false
​
#配置日志文件
logging.config=classpath:config/logback-spring.xml
​
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/shiro_demo?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 123123
​
#mybatis配置
mybatis.mapperLocations=classpath*:mapper/**/*.xml

二、数据准备

2.1、数据表创建:data.sql

2.2、实体对象创建

public class Permission {
    private Integer id;
    private Integer parent_id;
    private String parent_ids;
    private String permission;
    private String resource_type;
    private String url;
    private String name;
    private String available;
    private Set<Role> roles = new HashSet<>();
public class Role {
    private Integer id;
    private String role;
    private String description;
    private String available;
    private Set<User> users = new HashSet<>();
    private Set<Permission> permissions = new HashSet<>();
public class User {
    private Integer uid;
    private String username;
    private String password;
    private String name;
    private String id_card_num;
    private String state;
    private Set<Role> roles = new HashSet<>();
对象关系说明:
  • 1、一个User对应多个Role
  • 2、一个Permission对应多个Role
  • 3、一个Role对应多个User和多个Permission

2.3、mapper接口创建:

  • UserMapper:通过用户名获取用户操作,用户添加操作,用户删除操作
  • RoleMapper:通过用户id获取用户所有角色操作
  • PermissionMapper:通过用户所有角色获取用户所有权限操作
@Mapper
public interface UserMapper {
    User findByUserName(String userName);
​
    int insert(User user);
​
    int del(@Param("username") String username);
}
@Mapper
public interface RoleMapper {
​
    Set<Role> findRolesByUserId(@Param("uid") Integer uid);
​
}
@Mapper
public interface PermissionMapper {
    Set<Permission> findPermissionsByRoleId(@Param("roles") Set<Role> roles);
}
获取用户资料操作
  • 1、通过shiro登录成功
  • 2、通过用户名称调用UserMapper获取用户信息
  • 3、通过用户信息调用RoleMapper获取用户角色列表
  • 4、通过用户角色列表调用PermissionMapper获取用户权限列表

2.4、mapper文件

  • mapper接口的具体实现
  • PermissionMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.demo.mapper.PermissionMapper">
​
    <!-- 查询用户权限信息 -->
    <select id="findPermissionsByRoleId" resultType="com.demo.bean.Permission">
        SELECT p.* from sys_permission p LEFT JOIN sys_role_permission rp on p.id = rp.permission_id WHERE rp.role_id IN
        <foreach collection="roles" index="index" item="item" open="(" close=")" separator=",">
            #{item.id}
        </foreach>
    </select>
​
</mapper>
  • RoleMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.demo.mapper.RoleMapper">
​
    <!-- 查询用户信息 -->
    <select id="findRolesByUserId" resultType="com.demo.bean.Role">
        SELECT r.* from sys_role r LEFT JOIN sys_user_role ur on r.id = ur.role_id where ur.uid  = #{uid}
    </select>
​
</mapper>
  • UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.demo.mapper.UserMapper">
​
    <!-- 查询用户信息 -->
    <select id="findByUserName" resultType="com.demo.bean.User" parameterType="java.lang.String">
        SELECT * FROM user_info WHERE username = #{userName}
    </select>
    <!-- 添加用户 -->
    <!-- 创建用户 -->
    <insert id="insert" parameterType="com.demo.bean.User">
        <selectKey resultType="java.lang.Integer" keyProperty="uid" order="AFTER">
            SELECT
            LAST_INSERT_ID()
        </selectKey>
        insert into user_info
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="uid != null">
                uid,
            </if>
            <if test="username != null and username != ''">
                username,
            </if>
            <if test="password != null and password != ''">
                password,
            </if>
            <if test="name != null and name != ''">
                `name`,
            </if>
            <if test="id_card_num != null and id_card_num != ''">
                id_card_num,
            </if>
            <if test="state != null and state != ''">
                state,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="uid != null">
                #{uid},
            </if>
            <if test="username != null and username != ''">
                #{username},
            </if>
            <if test="password != null and password != ''">
                #{password},
            </if>
            <if test="name != null and name != ''">
                #{name},
            </if>
            <if test="id_card_num != null and id_card_num != ''">
                #{id_card_num},
            </if>
            <if test="state != null and state != ''">
                #{state},
            </if>
        </trim>
    </insert>
​
    <!-- 删除用户 -->
    <delete id="del">
        DELETE FROM user_info WHERE username = #{username}
    </delete>
</mapper>

2.5、service层:UserService

  • 通过调用mapper接口进行具体操作
@Service
public class UserService {
​
    @Autowired
    private UserMapper userMapper;
​
    public int insert(User user) {
        return userMapper.insert(user);
    }
​
    public int del(String username) {
        return userMapper.del(username);
    }
}

三、权限相关配置:ShiroRealm,ShiroConfig

  • ShiroRealm:shiro中通过Realm来获取用户的信息
  • ShiroConfig:定义访问规则
package com.shiro.realm;
​
import com.shiro.bean.Permission;
import com.shiro.bean.Role;
import com.shiro.bean.User;
import com.shiro.mapper.PermissionMapper;
import com.shiro.mapper.RoleMapper;
import com.shiro.mapper.UserMapper;
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.springframework.beans.factory.annotation.Autowired;
​
import java.util.Set;
​
/**
 * @author brusion
 * @date 2018/10/18
 * @description: Realm用于获取用户数据,组装后给shiro使用
 */
public class ShiroRealm extends AuthorizingRealm {
​
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private PermissionMapper permissionMapper;
​
​
    //验证当前登录的账号是否可用
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
​
        UsernamePasswordToken tokenUser = (UsernamePasswordToken) token;
        String username = tokenUser.getUsername();
        String password = new String(tokenUser.getPassword());
​
        User user = userMapper.getByName(username);
        if (user == null || !password.equals(user.getPassword())) {
            throw new UnknownAccountException("用户或密码为空");
        }
​
        if ("1".equals(user.getState())) {
            throw new LockedAccountException("账号被锁定");
        }
​
        return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }
​
    //获取当账号对应的权限,角色信息
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
​
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
​
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        Integer uid = user.getUid();
​
        Set<Role> roles = roleMapper.getRoleById(uid);
        for (Role role : roles) {
            info.addRole(role.getRole());
        }
​
        Set<Permission> permissions = permissionMapper.getPermissionByRole(roles);
        for (Permission per : permissions) {
            info.addStringPermission(per.getPermission());
        }
​
        return info;
    }
}

说明:
  • doGetAuthenticationInfo:用于验证用户账号密码是否正确,用户是否为可用账号
  • doGetAuthorizationInfo:通过mapper层获取用户数据,并封装到Realm中的通用对象中供realm中使用
package com.shiro.config;
​
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.shiro.realm.ShiroRealm;
import org.apache.shiro.mgt.SecurityManager;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
import java.util.LinkedHashMap;
​
/**
 * @author brusion
 * @date 2018/10/18
 * @description:
 */
@Configuration
public class ShiroConfig {
​
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
​
        //配置权限访问规则
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
​
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl("/login");
        factoryBean.setSuccessUrl("/index");
        factoryBean.setUnauthorizedUrl("/unauthorized");
​
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put("/login", "anon");
        map.put("/", "anon");
        map.put("/css/**", "anon");
        map.put("/img/**", "anon");
        map.put("/ijs/**", "anon");
        map.put("logout", "logout"); //退出登录
        map.put("/**", "authc"); //其他资源都需要登录
​
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }
​
    //事务管理器
    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(shiroRealm);
        return manager;
    }
​
    //生命周期处理器
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
​
    @Bean
    public ShiroRealm shiroRealm() {
        return new ShiroRealm();
    }
​
​
    //模板使用shiro标签
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
​
}

说明:
  • shiroFilter:配置主要的拦截规则如:页面设置,资源访问设置

四、请求操作

  • LoginController:登录相关操作及对应页面跳转
  • UserController:用于用户对应的接口操作
@Controller
public class LoginController {
​
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String userLogin(String username, String password, HttpSession session,Model model) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            User user = (User) subject.getPrincipal();
            model.addAttribute("user",user);
            session.setAttribute("user", user);
            return "index";
        } catch (Exception e) {
            return "login";
        }
    }
​
​
    @RequestMapping("/")
    public String root() {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        if (user == null) {
            return "redirect:/login";
        } else {
            return "redirect:/index";
        }
    }
​
    @RequestMapping("/login")
    public String login() {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        if (user == null) {
            return "login";
        } else {
            return "redirect:/index";
        }
    }
​
    @RequestMapping("/index")
    public String index(Model model) {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        if (user == null) {
            return "login";
        } else {
            model.addAttribute("user",user);
            return "index";
        }
    }
​
    @RequestMapping("/logout")
    public String logout(Model model) {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        model.addAttribute("msg", "安全退出");
        return "login";
    }
​
​
    /**
     * 无权限页面
     */
    @RequestMapping("/unauthorized")
    public String unauthorized() {
        return "unauthorized";
    }
​
}
​
@RestController
@RequestMapping("/user")
public class UserController {
​
    @Autowired
    private UserService userService;
​
    @RequiresPermissions("user:add")
    @RequestMapping("/add")
    public String add() {
        User user = new User();
        user.setName("test-name" + System.currentTimeMillis());
        user.setUsername("test-name");
        userService.insert(user);
​
        return "用户添加成功";
    }
​
    @RequiresPermissions("user:del")
    @RequestMapping("/del")
    public String delete() {
        userService.delete("test-name");
​
        return "用户删除成功";
    }
​
​
    @RequiresPermissions("user:add")
    @RequestMapping("/view")
    public String list() {
        return "列表数据";
    }
}
​

五、前端页面

  • index.xml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8" />
    <title>Insert title here</title>
</head>
<body>
<h1 th:text="'欢迎' + ${user.username } + '光临!请选择你的操作'"></h1><br/>
<ul>
    <h1 th:if="${msg != null }" th:text="${msg}" style="color: red"></h1>
​
    <shiro:hasPermission name="userInfo:add"><a href="/user/add">点击添加固定用户信息(后台写死,方便测试)</a></shiro:hasPermission><br/>
    <shiro:hasPermission name="userInfo:del"><a href="/user/del">点击删除固定用户信息(后台写死,方便测试)</a></shiro:hasPermission><br/>
    <shiro:hasPermission name="userInfo:view"><a href="/user/view">显示此内容表示拥有查看用户列表的权限</a></shiro:hasPermission><br/>
​
</ul>
<a href="/logout">点我注销</a>
</body>
</html>

  • login.xml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8" />
    <title>Insert title here</title>
</head>
<body>
<h1>欢迎登录</h1>
<h1 th:if="${msg != null }" th:text="${msg}" style="color: red"></h1>
<form action="/login" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>

  • unauthorized.xml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8" />
    <title>Insert title here</title>
</head>
<body>
<h1>对不起,您没有权限</h1>
</body>
</html>

六、启动测试

@SpringBootApplication
public class ShiroApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(ShiroApplication.class, args);
    }
}

6.2、测试

  • admin角色测试
地址:http://localhost:2001/
账号密码:admin  123456
登录成功操作:添加用户操作,查看用户操作(调用UserController接口数据)
退出登录操作:
  • test角色测试
地址:http://localhost:2001/
账号密码:test  123456
登录成功操作:删除用户操作(调用UserController接口数据)
退出登录操作:
源码地址:
Spring Boot是一个基于Spring框架的快速开发框架,通过提供一系列的开箱即用的功能和优化配置,简化了Java后台应用的开发流程。 Thymeleaf是一个Java模板引擎,用于在服务端渲染HTML页面。它可以和Spring Boot结合使用,通过在HTML页面中使用Thymeleaf的语法,实现页面的动态渲染和数据绑定。 Layui是一个国内比较流行的前端UI框架,提供了大量的CSS样式和JavaScript组件,可以快速构建美观、响应式的前端界面。 Apache Shiro是一个强大的开源安全框架,可以用于认证、授权和加密操作。它提供了对用户身份验证、角色和权限管理的支持,可以帮助开发者快速实现应用的安全控制。 Redis是一个高性能的内存数据库,常用于缓存和存储数据。它支持多种数据结构和操作,可以用于实现分布式锁、消息队列等功能,提高系统的性能和可扩展性。 MyBatis Plus是一个基于MyBatis框架的增强工具,提供了更简单、更便捷的数据库操作方式。它通过代码生成器和一系列的增强功能,简化了数据层的开发工作,提高了开发效率。 综上所述,可以使用Spring Boot作为后台框架,集成Thymeleaf实现页面渲染和数据绑定,使用Layui构建前端界面,使用Apache Shiro进行安全控制,使用Redis进行数据缓存和存储,使用MyBatis Plus进行数据库操作。这样搭建的后台系统可以实现高效、安全、可扩展的功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值