Shiro的使用(二):SpringBoot集成Shiro

一、基础环境搭建

1、新建SpringBoot项目,并导入以下依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </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>

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring5</artifactId>
        <version>3.0.11.RELEASE</version>
    </dependency>

    <!-- thymeleaf依赖 -->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-java8time</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>
</dependencies>

2、在templates目录下新建index.html,同时新建一个controller包,创建一个类MyController.java,如下:

index.html
在这里插入图片描述
MyController.java
在这里插入图片描述

3、启动该项目,在浏览器中输入控制器中的请求路径,可以看到访问的首页,环境搭建成功。

在这里插入图片描述

二、整合Shiro

1、导入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.6.0</version>
</dependency>

2、新建config包,在下面创建shiro配置类,如下:

@Configuration
public class ShiroConfig {

    //1、ShiroFilteFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //配置安全管理器
        factoryBean.setSecurityManager(securityManager);
        return factoryBean;
    }

    //2、DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getUserRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联自定义的realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //3、Realm
    @Bean
    public UserRealm getUserRealm() {
        return new UserRealm();
    }

    /**
     * 自定义realm
     */
    private class UserRealm extends AuthorizingRealm {

        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行授权操作");
            return null;
        }

        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行认证操作");
            return null;
        }
    }
}

3、新建页面add.htmlupdate.html,同时修改index.html,如下:

在这里插入图片描述
add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加用户</title>
</head>
<body>
<h1>添加用户</h1>
</body>
</html>

update.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改用户</title>
</head>
<body>
<h1>修改用户</h1>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a th:href="@{/user/add.html}">add</a>
<a th:href="@{/user/update.html}">update</a>
</body>
</html>

4、在MyController.java中新添加两个方法,用于跳转到add.html和update.html页面,然后启动项目。

@RequestMapping("/user/add.html")
    public String addUser(){
        return "user/add";
    }

    @RequestMapping("/user/update.html")
    public String updateUser(Model model){
        return "user/update";
    }

5、在浏览器中访问请求index.html页面,点击add和update,可以发现目前两个跳转的都可以跳转成功。

在这里插入图片描述

三、登录拦截

1、修改ShiroConfig中的getShiroFilterFactoryBean方法

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    //配置安全管理器
    factoryBean.setSecurityManager(securityManager);

    /**
     * anon:无需认证就可以访问
     * authc:必须认证了才能访问
     * user:必须拥有 记住我 才能用
     * perms:拥有某个资源的权限才能访问
     * role:拥有某个角色的权限才能反问
     */

    //添加shiro的内置过滤器
    Map<String, String> filterMap = new LinkedHashMap<>();
    filterMap.put("/user/add.html", "anon");
    filterMap.put("/user/update.html", "authc");
    factoryBean.setFilterChainDefinitionMap(filterMap);

    return factoryBean;
}

2、在MyController.java中新添加一个方法,用于跳转到登录页面,当访问没有认证时跳转到该页面。

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="">
    <p>账号:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit" value="登录"></p>
</form>
</body>
</html>

MyController

@RequestMapping("/user/add.html")
public String addUser(){
    return "user/add";
}

@RequestMapping("/user/update.html")
public String updateUser(Model model){
    return "user/update";
}

3、重启项目,点击add和update,可以发现,add可以访问,update不能访问,会跳转到登录页面,因为没有认证。

在这里插入图片描述

四、认证

1、在控制器中添加方法,用于登录

@RequestMapping("/user/login")
public String login(String username, String password, Model model) {
    //获得当前用户
    Subject subject = SecurityUtils.getSubject();
    //封装用户登录数据,生成令牌
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    try {
        subject.login(token);//如果没有异常登陆成功。跳转到首页
        return "index";
    }catch (UnknownAccountException e){
        model.addAttribute("msg", "账号错误");
        return "user/login";
    } catch (IncorrectCredentialsException e){
        model.addAttribute("msg", "密码错误");
        return "user/login";
    }
}

2、修改login.html,接收错误消息

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form th:action="@{/user/login}">
    <p>账号:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit" value="登录"></p>
    <p th:text="${msg}" style="color: red"></p>
</form>
</body>
</html>

3、修改自定义的UserRealm中重写的doGetAuthenticationInfo方法

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行认证操作");
    String username = "admin";
    String password = "admin";
    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    //用户名认证
    if (!username.equals(userToken.getUsername())){
        return null;//这里自动会抛出UnknownAccountException异常
    }
    //密码认证(会自动帮我们认证密码)
    return new SimpleAuthenticationInfo("", password, "");
}

4、重启项目后点击update,进行登录操作输入与我们设置的数据不一致,会提示登录失败。随后,可以在idea的控制台看到自定义的UserRealm中重写的doGetAuthenticationInfo方法输出的日志。

注意:这说明一个问题,当登录时调用subject.login(token)方法后,会去doGetAuthenticationInfo方法中进行登录信息的认证,由于与设置的用户名和密码不匹配,所以会出现认证失败的问题。在实际开发中,会在doGetAuthenticationInfo方法中进行登录用户的用户名和密码的验证(可以从数据库中获取数据进行对比).

在这里插入图片描述
在这里插入图片描述

五、整合MyBatis

项目结构如下

在这里插入图片描述

sql脚本如下:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50730
 Source Host           : localhost:3306
 Source Schema         : testdb

 Target Server Type    : MySQL
 Target Server Version : 50730
 File Encoding         : 65001

 Date: 20/09/2020 19:35:57
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `prams` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', 'root', 'user:add');
INSERT INTO `user` VALUES (2, 'test', 'test', 'user:add');

SET FOREIGN_KEY_CHECKS = 1;

1、添加依赖

<!-- 数据库连接 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 数据库连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.23</version>
</dependency>

<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<!-- 日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

2、application.yml配置

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/testdb?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    dbcp2:
      initial-size: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
mybatis:
  # 配置mapper.xml文件的位置
  mapper-locations: classpath:mapper/*.xml

3、创建实体类User

package com.xukun.shirodemo.entity;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
    private String prams;
}

4、创建UserMapper.java接口

package com.xukun.shirodemo.mapper;

import com.xukun.shirodemo.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper {
    User queryUserByName(String name);
}

5、创建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.xukun.shirodemo.mapper.UserMapper">

    <select id="queryUserByName" resultType="com.xukun.shirodemo.entity.User">
        select * from user where name = #{name}
    </select>
</mapper>

6、创建Service(这里偷个懒,没有创建接口)

package com.xukun.shirodemo.service;

import com.xukun.shirodemo.entity.User;
import com.xukun.shirodemo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    UserMapper userMapper;

    /**
     * 根据用户名获得用户信息
     *
     * @param name
     * @return
     */
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

7、在启动类上面使用@MapperScan注解,配置Mapper接口的位置

package com.xukun.shirodemo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.xukun.shirodemo.mapper")
public class ShirodemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShirodemoApplication.class, args);
    }
}

8、在ShiroConfig.java中注入UserService,实现从数据库中获取数据。

@Autowired
UserService userService;

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行认证操作");

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    //用户名认证
    User user = userService.queryUserByName(userToken.getUsername());
    if (user == null) {
        return null;//这里自动会抛出UnknownAccountException异常
    }
    //密码认证(会自动帮我们认证密码)
    return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}

9、再次重启项目,进行访问,可以看到登录效果。

六、授权

1、首先在控制器中添加一个方法,当某个资源未授权时我们调用这个方法。

@RequestMapping("/unanuthorized")
@ResponseBody
public String unanuthorized(){
    return "未经授权不得访问!";
}

2、修改ShiroConfig中getShiroFilterFactoryBean方法的内容

//添加shiro的内置过滤器
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/add.html", "perms[user:add]");
filterMap.put("/user/*.html", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);

//如果没有认证,跳转到登录页面进行登录认证
factoryBean.setLoginUrl("/user/login.html");

//如果没有授权跳转的请求地址
factoryBean.setUnauthorizedUrl("/unanuthorized");

3、重启项目,当登录后,点击add,浏览器会出现“未经授权不得访问”。

4、进行授权,修改UserRealm中doGetAuthorizationInfo方法的内容

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行授权操作");

    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    //用户在登陆的时候会认证,认证的时候会得到用户的权限
    Subject subject = SecurityUtils.getSubject();
    User user = (User) subject.getPrincipal();
    //设置当前登录用户的权限
    simpleAuthorizationInfo.addStringPermission(user.getPrams());

    return simpleAuthorizationInfo;
}

5、重启项目,当登录后,进行不同用户登录,观察现象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值