34 | Spring Boot整合Shiro框架(Shiro简介+实现登录拦截、用户认证、请求授权并整合Mybatis和Thymeleaf)

1. Shiro简介

1.1 什么是Shiro?
  • Apache Shiro 是一个功能强大且易于使用的 Java 安全框架
  • Apache Shiro 不仅可以用在JavaSE环境,也可以用在JavaEE环境
  • Apache Shiro 可执行身份验证、授权、加密、会话管理、Web集成、缓存等
  • 下载地址

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

1.2 有哪些功能?

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

1.3 Shiro架构(外部)

从外部来看Shiro,即从应用程序角度来观察如何使用shiro完成工作
在这里插入图片描述
在这里插入图片描述

1.4 Shiro架构(内部)

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

2. 10分钟快速上手

2.1 环境配置

1.导入日志相关依赖,版本自行加入

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
        </dependency>

        <!-- configure logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <scope>runtime</scope>
        </dependency>

2.配置文件 log4j.properties shiro.ini

2.2 代码分析

获取当前用户对象

Subject currentUser = SecurityUtils.getSubject();

通过当前用户拿到session

Session session = currentUser.getSession();

用户对象的常用方法:

	//判断当前用户是否被认证
	currentUser.isAuthenticated()
	//获得当前用户的一个认证
	currentUser.getPrincipal()
	//当前用户是否拥有xx角色
	currentUser.hasRole("schwartz")
    //已登录用户是否具有某种权限
    currentUser.isPermitted("lightsaber:wield")

令牌:

UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);//设置记住我功能
currentUser.login(token);//执行登录操作
currentUser.logout();//退出操作

3. springboot整合shiro

3.1 先新建一个spring boot的web项目,导入相应依赖
		<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.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.3.2.RELEASE</version>
 		</dependency>
3.2 编写配置类
3.2.1 自定义的 UserRealm 处理数据
package com.nsx.shito.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * Created by NXS
 * 2020/9/1 17:06
 */
//自定义的 UserRealm 处理数据
public class UserRealm extends AuthorizingRealm {
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        return null;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");
        return null;
    }
}
3.2.2 编写Shiro配置类
package com.nsx.shito.config;

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;

/**
 * Created by NXS
 * 2020/9/1 17:05
 */
@Configuration
public class ShiroConfig {
    //shiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        return factoryBean;
    }

    //DefaultWebSecurityManager 2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联userRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //创建realm对象,需要自定义类 1
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

在这里插入图片描述

3.2.3 控制层
package com.nsx.shito.controller;

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

/**
 * Created by NXS
 * 2020/9/1 16:08
 */
@Controller
public class LoginController {
    @RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("msg", "你好, shiro");
        return "index";
    }

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

    @RequestMapping("/user/update")
    public String update() {
        return "/user/update";
    }

}
3.2.4 页面

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>

<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a>
</body>
</html>

剩下的自己自定义吧。。。。

4. shiro实现登录拦截

4.1 修改Shiro配置类
//shiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        /*
            anon:无需认证就可以访问
            authc:必须认证了才能访问
            user:必须拥有 记住我功能才能用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
        */
        //添加shiro的内置过滤器
        Map<String, String> fiterMap = new LinkedHashMap<>();
        // /user/* 指的是路径
        fiterMap.put("/user/*", "authc");
        factoryBean.setFilterChainDefinitionMap(fiterMap);
        // 设置登录的请求
        factoryBean.setLoginUrl("/login");
        return factoryBean;
    }
4.2 控制层
@RequestMapping("/login")
    public String login() {
        return "login";
    }
4.3 登录页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>

<body>
<form action="">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit"></p>
</form>
</body>
</html>

此时点击页面,发现点add update会跳转到一个登录页面,这是由于没有权限。

5. shiro实现用户认证并整合Mybatis

5.1 导入依赖
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
5.2 创建数据库
5.2.1 配置druid数据源
spring:
  datasource:
    #   数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm_crud
    type: com.alibaba.druid.pool.DruidDataSource
    #   数据源其他配置
    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,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
5.2.2 连接数据库

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

5.3 创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}
5.4 配置映射文件
mybatis:
  type-aliases-package: com.nsx.shiro
  mapper-locations: classpath:mapper/*.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.nsx.shiro.dao.UserMapper">
    <select id="getUserByName" parameterType="String" resultType="User">
        select *
        from user
        where name = #{name}
    </select>

</mapper>
5.5 编写dao层、Service层
package com.nsx.shiro.dao;

import com.nsx.shiro.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * Created by NXS
 * 2020/9/2 12:15
 */
@Repository
@Mapper
public interface UserMapper {
    User getUserByName(String name);
}
package com.nsx.shiro.service;

import com.nsx.shiro.pojo.User;

/**
 * Created by NXS
 * 2020/9/2 12:24
 */

public interface UserService {
    User getUserByName(String name);
}

package com.nsx.shiro.service;

import com.nsx.shiro.dao.UserMapper;
import com.nsx.shiro.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by NXS
 * 2020/9/2 12:24
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}

5.6 在控制层加入登录时用户认证的方法
@RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }
    
@RequestMapping("/login")
    public String login(String name, String password, Model model) {
        // 获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        // 封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
        try {
            // 执行登录方法, 如果没有异常就说明用户和密码完全正确
            subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            // 用户名不存在
            model.addAttribute("msg", "用户名错误");
            return "login";
        } catch (IncorrectCredentialsException e) {
            // 密码错误
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }
5.7 在UserRealm数据处理类中连接数据库进行判断认证
package com.nsx.shiro.config;

import com.nsx.shiro.pojo.User;
import com.nsx.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by NXS
 * 2020/9/1 17:06
 */
//自定义的 UserRealm 处理数据
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

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

    @Override
    // 认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");
        // 封装用户的登录数据
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // 连接数据库
        User user = userService.getUserByName(userToken.getUsername());
        // 没有此人,抛出 UnkownAccountException
        if (user == null) {
            return null;
        }
        /*三个参数:
            principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
            一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
            credentials: 传递的密码对象
            realmName: 认证名(指定当前 Realm 的类名)
        */
        // 密码认证, shiro自己做
        return new SimpleAuthenticationInfo("", user.getPassword(), "");
    }
}
5.8 登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>

<body>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit"></p>
</form>
</body>
</html>

👌👌👌,认证已经做完了,但还有一个功能需要加,那就是MD5盐值加密了,这个功能写到了这篇文章

那么认证完,就是说可以登进网页,但进入网页后,得根据用户有哪些权限去访问能访问的东西,所以接下来讲讲如何授权。

6. shiro实现请求授权

6.1 先在数据库中再添加一个权限字段

6.2 添加一个未授权的方法
@RequestMapping("/unauthorize")
    @ResponseBody
    public String unauthorize() {
        return "你没有权限访问此页面噢";
    }
6.3 修改ShiroConfig和UserRealm配置类

ShiroConfig

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

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

        // 设置哪些路径授予哪些权限
        fiterMap.put("/user/add", "perms[user:add]");
        fiterMap.put("/user/update", "perms[user:update]");

        // /user/* 指的是路径 必须认证了才能访问
        fiterMap.put("/user/*", "authc");
        factoryBean.setFilterChainDefinitionMap(fiterMap);

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

        // 跳转至未授权页面
        factoryBean.setUnauthorizedUrl("/unauthorize");
        return factoryBean;
    }

UserRealm

//自定义的 UserRealm 处理数据
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    @Override
    // 授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //拿到当前登录的用户对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPermission());
        return info;
    }

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

        // 封装用户的登录数据
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        // 连接数据库
        User user = userService.getUserByName(userToken.getUsername());

        // 没有此人,抛出 UnkownAccountException
        if (user == null) {
            return null;
        }

        /*三个参数:
            principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
            一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
            credentials: 传递的密码对象
            realmName: 认证名(指定当前 Realm 的类名)
        */
        // 密码认证, shiro自己做
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
}

7. shiro整合Thymeleaf

7.1 整合思路
  1. 一开始进首页,显示登录按钮
  2. 用户登录后,只显示能看的内容
  3. 登录按钮消失,注销按钮出现
7.2 导入依赖
		<dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
7.3 修改Shiro配置创建ShiroDialect对象
	// ShiroDialect 用来整合shiro和thymleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
7.4 修改UserRealm配置把用户对象存放到Session中
@Override
    // 认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");

        // 封装用户的登录数据
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        // 连接数据库
        User user = userService.getUserByName(userToken.getUsername());

        // 没有此人,抛出 UnkownAccountException
        if (user == null) {
            return null;
        }

        //获取当前登录的用户对象
        Subject subject = SecurityUtils.getSubject();
        
        //存放到Session中
        Session session = subject.getSession();
        session.setAttribute("user",user);

        /*三个参数:
            principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
            一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
            credentials: 传递的密码对象
            realmName: 认证名(指定当前 Realm 的类名)
        */
        // 密码认证, shiro自己做
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
7.5 注销功能实现

控制层添加一个注销的方法

	@RequestMapping("/logout")
    public String logout() {
        // 获取当前的用户
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser.isAuthenticated()) {
            currentUser.logout();
        }
        return "redirect:/toLogin";
    }
7.6 修改index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>

<body>
<h1>首页</h1>
<div th:if="${session.user==null}">
    <a th:href="@{/toLogin}">登录</a>
</div>
<div th:if="${session.user!=null}">
    <a th:href="@{/logout}">注销</a>
</div>
<p th:text="${msg1}"></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>

完,最后感谢你看完这篇文章😊😊😊

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值