Spring Boot学习笔记-Spring Boot整合Shiro(二)

Spring Boot学习

官网:https://spring.io/projects/spring-boot#overview

文档:https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/

参考视频:【狂神说Java】SpringBoot最新教程IDEA版通俗易懂_哔哩哔哩_bilibili

项目完整参考代码:lexiaoyuan/SpringBootStudy: My Spring Boot study notes (github.com)SpringBootStudy: 我的Spring Boot学习笔记 (gitee.com)

Spring Boot学习笔记-Spring Boot整合Shiro(一)

【补充】安装Easy Code插件

介绍:https://gitee.com/makejava/EasyCode

  • 在设置中的插件市场,搜索Easy Code,点击安装,安装完成后重启IDEA即可。(我这里已经安装过了)

Spring Boot学习笔记-Spring Boot整合Shiro(二)

整合MyBatis

  • 首先,打开mysql,确保idea能连接上。

  • pom.xml中导入一些依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.2</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.22</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
  • 使用Easy Code插件生成代码,按下图操作即可

Spring Boot学习笔记-Spring Boot整合Shiro(二)
Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 生成代码后的目录,其中UserServiceImpl.java的目录自己做了调整

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 配置一下数据源和MyBatis,在resources目录下新建一个application.yml文件
# 配置数据源
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource  # 自定义数据源

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# MyBatis配置
mybatis:
  type-aliases-package: com.springboot.entity
  mapper-locations: classpath:mapper/*.xml
  • User.java中增加一个toString方法
/**
 * (User)实体类
 */
public class User{

    private Integer id;
    
    private String name;
    
    private String pwd;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  • UserDao.java中增加一些代码
/**
 * (User)表数据库访问层
 */
@Repository
@Mapper
public interface UserDao {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    User queryById(Integer id);

    User queryByName(String name);

    /**
     * 查询指定行数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    List<User> queryAllByLimit(@Param("offset") int offset, @Param("limit") int limit);


    /**
     * 通过实体作为筛选条件查询
     *
     * @param user 实例对象
     * @return 对象列表
     */
    List<User> queryAll(User user);

    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 影响行数
     */
    int insert(User user);

    /**
     * 修改数据
     *
     * @param user 实例对象
     * @return 影响行数
     */
    int update(User user);

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 影响行数
     */
    int deleteById(Integer id);

}
  • UserService.java中也增加一个queryByName方法
/**
 * (User)表服务接口
 */
public interface UserService {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    User queryById(Integer id);

    User queryByName(String name);

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    List<User> queryAllByLimit(int offset, int limit);

    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    User insert(User user);

    /**
     * 修改数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    User update(User user);

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    boolean deleteById(Integer id);

}
  • UserServiceImpl.java中也增加一个queryByName方法
/**
 * (User)表服务实现类
 */
@Service("userService")
public class UserServiceImpl implements UserService {
    @Resource
    private UserDao userDao;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public User queryById(Integer id) {
        return this.userDao.queryById(id);
    }

    @Override
    public User queryByName(String name) {
        return this.userDao.queryByName(name);
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    @Override
    public List<User> queryAllByLimit(int offset, int limit) {
        return this.userDao.queryAllByLimit(offset, limit);
    }

    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    @Override
    public User insert(User user) {
        this.userDao.insert(user);
        return user;
    }

    /**
     * 修改数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    @Override
    public User update(User user) {
        this.userDao.update(user);
        return this.queryById(user.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer id) {
        return this.userDao.deleteById(id) > 0;
    }
}
  • UserDao.xml中也增加一个queryByName的SQL语句
<?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.springboot.dao.UserDao">

    <resultMap type="com.springboot.entity.User" id="UserMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="pwd" column="pwd" jdbcType="VARCHAR"/>
    </resultMap>

    <!--查询单个-->
    <select id="queryById" resultMap="UserMap">
        select
          id, name, pwd
        from mybatis.user
        where id = #{id}
    </select>

    <select id="queryByName" parameterType="String" resultType="user">
        select
          id, name, pwd
        from mybatis.user
        where name = #{name}
    </select>

    <!--查询指定行数据-->
    <select id="queryAllByLimit" resultMap="UserMap">
        select
          id, name, pwd
        from mybatis.user
        limit #{offset}, #{limit}
    </select>

    <!--通过实体作为筛选条件查询-->
    <select id="queryAll" resultMap="UserMap">
        select
          id, name, pwd
        from mybatis.user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="name != null and name != ''">
                and name = #{name}
            </if>
            <if test="pwd != null and pwd != ''">
                and pwd = #{pwd}
            </if>
        </where>
    </select>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into mybatis.user(name, pwd)
        values (#{name}, #{pwd})
    </insert>

    <!--通过主键修改数据-->
    <update id="update">
        update mybatis.user
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="pwd != null and pwd != ''">
                pwd = #{pwd},
            </if>
        </set>
        where id = #{id}
    </update>

    <!--通过主键删除-->
    <delete id="deleteById">
        delete from mybatis.user where id = #{id}
    </delete>

</mapper>
  • 首先在测试类里测试一下
@SpringBootTest
class Springboot08ShiroApplicationTests {

    @Autowired
    private UserServiceImpl userService;

    @Test
    void contextLoads() {
        System.out.println(userService.queryById(1).toString());
    }

}
  • 运行测试类,可以看到,没有问题。

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 修改UserRealm.java里的认证方法,通过数据库查询用户
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行了===》认证doGetAuthorizationInfo");

    UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

     // 用户名、密码认证,从数据库取
    User user = userService.queryByName(userToken.getUsername());
    
    if (user == null)  // 没有该用户(名)
       return null;  // 会抛出异常 UnknownAccountException

    // 密码认证,shiro做
    // 可以加密:MD5、MD5盐值加密
    return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
  • 再运行web项目,访问:http://localhost:8080/toLogin,输入错误的用户名和密码依然可以提示相关信息,同时,输入数据库中user表中的用户名和密码,则可以登录成功

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • OK,整合MyBatis和Druid完成!

请求授权

  • ShiroConfig.java中的方法里添加授权规则
// 3. 创建ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    // 设置安全管理器
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

    // 添加shiro的内置过滤器
    /**
     * anon: 无需认证就可以访问
     * authc: 必须认证了才能访问
     * user: 必须有记住我功能才能访问
     * perms: 拥有对某个资源的权限才能访问
     * role:拥有某个角色权限才能访问
     */
    // 登录拦截
    Map<String, String> filterMap = new LinkedHashMap<>();

    // 授权 (要放在前面)
    filterMap.put("/user/add", "perms[user:add]");  // 有add权限才能访问add页面
    filterMap.put("/user/update", "perms[user:update]"); // 有update权限才能访问update页面

    // 拦截请求
    filterMap.put("/user/*", "authc");  // 支持通配符

    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

    // 设置登录请求
    shiroFilterFactoryBean.setLoginUrl("/toLogin");

    return shiroFilterFactoryBean;
}
  • 测试:运行一下项目,访问:http://localhost:8080/toLogin,输入正确的用户名和密码(比如:乐小猿,admin)后,登录,再点击add或update链接,发现未授权,

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 配置未授权的页面:
  • MyController.java中增加一个方法
@GetMapping("/unauthorized")
@ResponseBody
public String unauthorized() {
    return "没有权限访问该页面";
}
  • 在上面设置登录请求之后再设置未授权的页面
// 设置未授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
  • 重复上面的测试,可以看到,进入了自定义的未授权的页面

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 给用户添加权限:在UserRealm.java中的授权方法中进行授权
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行了===》授权doGetAuthorizationInfo");

    // 进入这个方法就添加权限给用户
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.addStringPermission("user:add");

    return authorizationInfo;
}
  • 重复上面的测试,可以看到,登录成功后可以访问add页面

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 通过数据库得到当前用户的权限:
  • 首先修改一下user表,增加一列

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 然后用Easy Code重新生成一下实体类User.java(自己加了个toString方法)和UserDao.xml(自己加了个queryByName的SQL语句)
/**
 * (User)实体类
 */
public class User implements Serializable {
    private static final long serialVersionUID = 227972241159940478L;
    
    private Integer id;
    
    private String name;
    
    private String pwd;
    
    private String perms;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getPerms() {
        return perms;
    }

    public void setPerms(String perms) {
        this.perms = perms;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                ", perms='" + perms + '\'' +
                '}';
    }
}
<?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.springboot.dao.UserDao">

    <resultMap type="com.springboot.entity.User" id="UserMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="pwd" column="pwd" jdbcType="VARCHAR"/>
        <result property="perms" column="perms" jdbcType="VARCHAR"/>
    </resultMap>

    <!--查询单个-->
    <select id="queryById" resultMap="UserMap">
        select
          id, name, pwd, perms
        from mybatis.user
        where id = #{id}
    </select>

    <select id="queryByName" parameterType="String" resultType="user">
        select
          id, name, pwd, perms
        from mybatis.user
        where name = #{name}
    </select>

    <!--查询指定行数据-->
    <select id="queryAllByLimit" resultMap="UserMap">
        select
          id, name, pwd, perms
        from mybatis.user
        limit #{offset}, #{limit}
    </select>

    <!--通过实体作为筛选条件查询-->
    <select id="queryAll" resultMap="UserMap">
        select
          id, name, pwd, perms
        from mybatis.user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="name != null and name != ''">
                and name = #{name}
            </if>
            <if test="pwd != null and pwd != ''">
                and pwd = #{pwd}
            </if>
            <if test="perms != null and perms != ''">
                and perms = #{perms}
            </if>
        </where>
    </select>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into mybatis.user(name, pwd, perms)
        values (#{name}, #{pwd}, #{perms})
    </insert>

    <!--通过主键修改数据-->
    <update id="update">
        update mybatis.user
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="pwd != null and pwd != ''">
                pwd = #{pwd},
            </if>
            <if test="perms != null and perms != ''">
                perms = #{perms},
            </if>
        </set>
        where id = #{id}
    </update>

    <!--通过主键删除-->
    <delete id="deleteById">
        delete from mybatis.user where id = #{id}
    </delete>

</mapper>
  • 修改一下UserRealm.java中的认证和授权的方法
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行了===》授权doGetAuthorizationInfo");

    // 进入这个方法就添加权限给用户
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    //authorizationInfo.addStringPermission("user:add");

    // 拿到当前的对象
    Subject subject = SecurityUtils.getSubject();
    User currentUser = (User) subject.getPrincipal();  // 拿到下面认证方法中传上来的user对象

    // 添加当前用户的权限,通过数据库得到当前用户的权限
    authorizationInfo.addStringPermission(currentUser.getPerms());

    return authorizationInfo;
}

// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行了===》认证doGetAuthorizationInfo");

    UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
    
    // 用户名、密码认证,从数据库取  
    User user = userService.queryByName(userToken.getUsername());

    if (user == null)  // 没有该用户(名)
       return null;  // 会抛出异常 UnknownAccountException
    
    // 把从数据库中查到的user传递到上面授权的方法
    return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
  • 测试:访问:http://localhost:8080/toLogin,使用数据库中user表中的用户名和密码登录,可以看到,只有个拥有一定权限的用户才能访问其能访问的页面。测试OK!
  • 请求授权完成!

shiro整合thymeleaf

官网文档:https://github.com/theborakompanioni/thymeleaf-extras-shiro

  • pom.xml中添加整合依赖
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
  • ShiroConfig.java中整合shiro和thymeleaf
// 整合ShiroDialect:用来整合shiro和thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
}
  • index.html中添加判断
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <h1>首页</h1>
  <p th:text="${msg}"></p>

  <p shiro:notAuthenticated="">
    <a th:href="@{/toLogin}">登录</a>
  </p>

  <div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
  </div>

  <div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
  </div>

</body>
</html>
  • 重新运行项目,访问:http://localhost:8080/,没有登录时只显示登录

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 点击登录,用乐小猿的账户登录,只有add权限,就只显示add页面

Spring Boot学习笔记-Spring Boot整合Shiro(二)

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • 用beta的账户登录,只有update权限,就只显示update页面

Spring Boot学习笔记-Spring Boot整合Shiro(二)

Spring Boot学习笔记-Spring Boot整合Shiro(二)

  • OK,整合完成!
上一篇:Docker--------修改Docker0网桥默认网段


下一篇:spring boot 整合 shiro 权限框架