Sringboot2整合shiro实现登录认证和记住我功能

news2025/1/17 0:56:03

Sringboot2整合shiro实现及登录认证和记住我

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • Sringboot2整合shiro实现及登录认证和记住我
  • 前言
  • 一、关于Shiro
  • 二、功能实现
    • 1.创建User表的数据库
    • 2.引入依赖
    • 3.配置application.yaml文件
    • 4.完善Mybatis
    • 5.重写Realm和Shiro配置类
    • 6.前端和Controller层


前言

提示:这里可以添加本文要记录的大概内容:

很久没写过文章了,最近课程设计硬性要求要使用Spring Secure或shiro其中一种安全框架,因为shiro的轻便更被广泛使用,所以学了一下shiro框架的基本使用,也来了点兴致,所以记录一下。


提示:以下是本篇文章正文内容,下面案例可供参考

一、关于Shiro

  1. Apache Shiro 是一个功能强大且易于使用的Java安全框架,为开发人员提供了一种直观而全面的身份验证,授权,加密和会话管理解决方案,提供了身份验证、授权、密码学、会话管理四大基本安全功能、还提供一些缓存、记住我、并发等多种额外功能解决一些问题。
  2. Shiro的一些核心概念有:Realms、SecutiryManager、Subjuect。
  3. 关于Subject可直接理解为对象,用于和应用进行交互,通过Subject传入到安全管理器。
  4. 关于SecurityManger就是安全管理器,所有的安全性操作都与它进行交互,是Shiro管理的核心。
  5. 关于Realms可以理解为存储域,存储Shiro的一些安全数据等,安全管理器需要从Realms获取数据,验证是否合法。

大概就这些,学的比较粗浅,底层源码也没咋了解,直接就学了如何使用。

二、功能实现

1.创建User表的数据库

  1. 实现Shiro与数据库交互,能够通过手机号+密码或邮箱+密码进行登录验证。
    在这里插入图片描述

这里的数据库字段设置如上、Id号(主键)、name(姓名)、phone(手机号)、username(用户名)、password(密码)、email(邮箱)、state(状态)。

2.引入依赖

pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>shiroDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>shiroDemo</name>
	<description>shiroDemo</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.17</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>1.10.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.10.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

3.配置application.yaml文件

当然也可以配置application.properties,个人习惯使用yaml,这里配置mybatis和连接mysql数据库,如下:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/teamwork?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  #config-location: classpath:mybatis/mybatis-config.xml  #全局配置文件位置
  mapper-locations: classpath:mybatis/mapper/*.xml  #sql映射文件位置
  configuration:
    map-underscore-to-camel-case: true

4.完善Mybatis

  1. 创建User类如下:
package com.example.shirodemo.Bean;

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

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private Integer id;
    private String name;
    private String phone;
    private String username;
    private String password;
    private String email;
    private Integer state;
}
  1. 创建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.example.shirodemo.mapper.UserMapper">
</mapper>
package com.example.shirodemo.mapper;

import com.example.shirodemo.Bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {
    @Select("select password from user where phone=#{phone} ")
    User Login(String phone);
    @Select("select password from user where email=#{email}")
    User LoginByEmail(String email);
}

这里用两个Select语句,当前端传入数据第一种方式查询不到user用户时,就进行第二种,以此来实现两种登录方式,当然也可以一个方法直接写入到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.example.homework1.mapper.UserMapper">
    <select id="login" resultType="com.example.homework1.bean.User">
        select *from User
    <where>
            <if test="phone!=null and phone!=''">
               and phone=#{phone}
            </if>
            <if test="email!=null and email!=''">
               and email=#{email}
            </if>
            and password= #{password}
    </where>
    </select>

5.重写Realm和Shiro配置类

  1. 因为数据库中存储的密码是用的base64+md5加密存储,所以这里要先写一个MD5的加密类,如下:
package com.example.shirodemo.config;


import org.springframework.util.DigestUtils;

import java.util.Base64;

import static com.mysql.cj.util.StringUtils.getBytes;

public class MD5 {
    /**
     * @param text明文
     * @param key密钥
     * @return 密文
     * 对数据库内的密码存储进行md5的加密
     */
    public static String md5(String password,String key) throws Exception {
            String base64encodedString = Base64.getEncoder().encodeToString((password + key).getBytes("utf-8"));
            // 加密后的字符串
            return DigestUtils.md5DigestAsHex(getBytes(base64encodedString));


        }

}
  1. 重写Realm方法
package com.example.shirodemo.config;

import com.example.shirodemo.Bean.User;
import com.example.shirodemo.mapper.UserMapper;
import com.example.shirodemo.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;


public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserMapper usermapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {
        String username= (String) authcToken.getPrincipal();
        User user;
        user = usermapper.Login(username);
        if (user == null) {
            user =usermapper.LoginByEmail(username);
            if(user==null)
                throw new UnknownAccountException("用户不存在");
        }
        return new SimpleAuthenticationInfo(user, user.getPassword(),getName());
    }

}

这里主要重写两个方法,第一个方法用于进行权限验证,这里用不到,下面一个方法用于进行登录验证,主要从Token中取得用户名,然后是否存在此用户,不存在则报错,存在则交给CredentialsMatcher进行密码进行密码匹配

  1. ShiroConfiguration类
package com.example.shirodemo.config;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfiguration {
    /**
     解决MyRealm中UserMapper一直是null的问题
     **/
    @Bean
    MyRealm myRealm() {
        return new MyRealm();
    }
    /**
     *安全管理器
     * **/
    @Bean
    SecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        manager.setRememberMeManager(rememberMeManager());
        manager.setSessionManager(sessionManager());
        return manager;
    }

    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //指定 SecurityManager
        bean.setSecurityManager(securityManager());
        //登录页面
        bean.setLoginUrl("/login");
        //登录成功页面
        bean.setSuccessUrl("/");
        //访问未获授权路径时跳转的页面
        bean.setUnauthorizedUrl("/login");
        //配置路径拦截规则,注意,要有序
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/doLogin", "anon");
        map.put("/logout","logout");
        map.put("/**", "user");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }
    /**
     但是再shiro:1.6开始,新增了一个InvalidRequestFilter的过滤器,用于拦截存在安全问题的uri,不进行配置在首次访问/时,URL中会出现jsessionid,并返回400错误
     * **/
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return  sessionManager;
    }
    /**设置Cookie**/
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(86400);
        return cookie;
    }

    /**
     * 设置cookie管理对象和Cookie的加密密钥,默认是AES加密
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

}

将logout写入filterChainDefinitionMap链中就可以自动实现注销功能,注意UserMapper类和跳转被拦截的问题即可。

在这里插入图片描述

6.前端和Controller层

  1. Controller层——IndexController
package com.example.shirodemo.Controller;

import com.example.shirodemo.Bean.User;
import com.example.shirodemo.config.MD5;
import com.example.shirodemo.mapper.UserMapper;
import com.example.shirodemo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
public class IndexController {
    @GetMapping("/login")
    public String login(){
        return "login";
    }

    @GetMapping("/")
    public String index(Model model) {
        model.addAttribute("message", "登录成功");
        return "index";
    }

    @PostMapping("/doLogin")
    public String doLogin(String username, String password, boolean rememberMe,Model model){
        Subject subject= SecurityUtils.getSubject();
        try {
            String surepassword= MD5.md5(password,"Aiwin");
            UsernamePasswordToken token=new UsernamePasswordToken(username, surepassword);
            token.setRememberMe(rememberMe);
            subject.login(token);
            model.addAttribute("message","登录成功");
            return "index";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            model.addAttribute("message","登录失败");
            return "login";
        } catch (Exception e) {
            e.printStackTrace();
            return  "login";
        }
    }

}

主要是Suject类,将接受到的用户和正确的密码提交到Token,通过与Subject类进行交互进行login,MyRealm中取得的用户名和密码可以理解为就是从这里Token交上去的。

  1. 前端页面
    Index.html:
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<label style="color: red" th:text="${message}"></label>
<a th:href="@{/logout}">注销</a>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form  th:action="@{/doLogin}" method="post">
        <label style="color: red" th:text="${message}"></label>
        <input type="text" name="username"  placeholder="用户名" autocomplete="off">
        <input type="password" name="password" class="form-control" placeholder="密码" autocomplete="off">
        <p><input type="checkbox" name="rememberMe" />&nbsp;&nbsp;记住我</p>
         <button type="submit">提交</button>
</form>
</body>
</html>

至此,功能全部实现,实现效果图如下

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

直接关闭浏览器,然后再访问/,可以直接跳转到登录成功的页面,点击注销再访问则会跳转到登录页面,应该是通过Cookie中的rememberMe中取得user的信息进行验证判断是否通过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

resolution-robust large mask inpainting with fourier convolutions

Resolution-robust Large Mask Inpainting with Fourier Convolutions(2021)_studyeboy的博客-CSDN博客_分辨率稳健的大掩膜修复[Paper] Resolution-robust Large Mask Inpainting with Fourier Convolutions(2021)[Code]saic-mdal/lama基于傅里叶卷积的分辨率稳健的大型掩码修…

数据结构之排序【归并排序和快排的顶级优化和快排的三种原理的实现及分析】 内含动态演示图

文章目录引言&#xff1a;1.归并排序(MergeSort)2.快速排序的优化&#xff08;顶级优化&#xff09;3.快速排序的三种思路的代码实现及分析4.归并排序和快排第3原理的测试引言&#xff1a; 刚刚去回顾了一下递归实现的几个小代码&#xff0c;感觉递归真的是很神奇的一个东西&a…

C# StringBuilder

StringBuilder位于命名空间System.Text下&#xff0c;使用前需引入 using System.Text; StringBuilder的构造 new StringBuilder(string value) StringBuilder sb1 new StringBuilder("www.abc.com"); 利用构造函数创建一个值为“www.abc.com”的StringBuilder…

解决Ubuntu不能上网以及无法远程连接Ubuntu

本文环境 物理机OS&#xff1a; Windows10 专业版 虚拟机平台&#xff1a; VMware Workstation 16 Pro 虚拟机OS&#xff1a; Ubuntu 20.04 相信大家在使用Ubuntu中也有遇到不能上网&#xff0c;我也是尝试了很多的方法都不行&#xff0c;终于找到了一种可行的方法。 步骤…

测试开发应该具备的六大能力

前言 前几天一个前同事找我聊了个问题&#xff1a;一个好的测试开发同学需要具备哪些能力&#xff1f;我思考了一下&#xff0c;给了他如下答复&#xff1a; 从我工作中接触到的测试开发&#xff0c;以及面试测试开发候选人时问的问题&#xff0c;我将自己对测试开发这个岗位…

Chrome插件开发

1.什么是 Chrome 插件 谷歌浏览器插件是一种小型的定制浏览器体验的程序&#xff0c;通过插件可以自定义浏览器的一些行为来适合个人的需求&#xff0c;例如上面的查看服务器状态插件。 在应用商店中下载下来的插件基本上都是以.crx 为文件后缀&#xff0c;该文件其实就是一个…

实验一 课本第三章MongoDB数据库操作3.1-3.7

一、实验目的&#xff1a; 掌握MongoDb的部署 熟悉数据库和集合操作 二、实验环境&#xff1a; 一台运行的计算机 Linux平台 SecureCRT平台 三、实验内容&#xff1a; 3.1MongoDB部署 1.MongonDb部署&#xff08;windows平台&#xff09; &#xff08;1&#xff09;下载Mongo…

Node环境安装

Node的版本管理工具工具介绍gnvm官网指出特色的地方安装验证配置与使用配置文件内容命令使用nvm安装脚本命令下载请求文件下载验证配置文件使用n安装使用Fast Node Manager (fnm)安装使用工具介绍 本文介绍四款 Node 版本管理工具&#xff0c;用于下载和切换对应的 Node 与 Npm…

【金猿人物展】极盾科技CEO丁杨:让数据安全回归场景、业务和价值

‍丁杨本文由极盾科技CEO丁杨撰写并投递参与“数据猿年度金猿策划活动——2022大数据产业趋势人物榜单及奖项”评选。‍数据智能产业创新服务媒体——聚焦数智 改变商业我们的数据安全&#xff0c;还是以前的安全么&#xff1f;传统数据安全方案依赖网络和数据库安全能力进行围…

2022年安徽建筑八大员(标准员)考试试题及答案

百分百题库提供建筑八大员&#xff08;标准员&#xff09;考试试题、建筑八大员&#xff08;标准员&#xff09;考试真题、建筑八大员&#xff08;标准员&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 22.某设计单位对承接…

Vue2.x + Echarts实现知识图谱数据渲染

代码案例数据写死了&#xff0c;后端Java可使用SpringBootNeo4j查询数据返回。 <template><div id"myChart"></div> </template><style> #myChart {width: 100%;height: 1000px; } </style> <script>export default {nam…

视频号小店是什么?如何开通视频号小店API?

微信视频号于2022年7月正式推出“视频号小店“服务&#xff0c;为商家提供商品信息服务、商品交易&#xff0c;支持商家在视频号运营电商。目前视频号小店有个体工商户或企业资质的商家进行开店&#xff0c;企业店需要企业营业执照认证、个体工商户则需要个体工商户营业执照认证…

Android设计模式详解之模板方法模式

前言 定义&#xff1a;定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤&#xff1b; 使用场景&#xff1a; 多个子类有公有的方法&#xff0c;并且逻辑基本相同时&#xff1b;…

Linux环境下挂载外接硬盘

一. 前言 调试ARTIK过程中&#xff0c;首次外接硬盘不会自动挂载&#xff0c;需要将硬盘挂载到系统文件夹下方能读取硬盘内容&#xff0c;因此对于Ubuntu系统下挂载硬盘和开机自动挂载外接硬盘配置的方法进行总结。 二. 挂载外接硬盘步骤 通过命令 fdisk -l 查看硬盘资源信息…

Hive+Spark离线数仓工业项目--ODS层及DWD层构建(2)

ODS层构建&#xff1a;代码导入 目标&#xff1a;实现Python项目代码的导入及配置 实施 Oracle本地驱动目录**&#xff1a;将提供的**instantclient_12_2**目录放入D盘的根目录下 PyHive本地连接配置&#xff1a;将提供的CMU目录放入C盘的根目录下 auto_create_hive_table包…

Java中的Map集合体系

Map集合体系Map集合的概述Map集合体系特点Map集合常用APIMap集合的遍历方式&#xff1a;方式一&#xff1a;键找值方式二&#xff1a;键值对方式三&#xff1a;lambda表达式Map集合的实现类HashMapMap集合的实现类TreeMap集合嵌套Map集合的概述 Map集合概述和使用&#xff1a;…

Java 基础:变量、操作符、代码块和控制流

目录 一、变量&#xff1a;Variables 1、基本数据类型 2、数组 二、操作符/运算符 Operators 三、表达式、语句和代码块 四、程序控制流 一、变量&#xff1a;Variables Java 定义了以下几种变量&#xff1a; 实例变量/成员变量&#xff08;非静态字段&#xff09;&…

Docker+NETCore系列文章(五、推送自制镜像到Docker Hub、阿里云镜像仓库)

推送镜像到Docker Hub镜像仓库 1、访问Docker Hub&#xff1a;https://hub.docker.com/&#xff0c;注册并登陆Docker。 2、使用docker pull hello-world命令拉取hello-workld镜像。 [rootVM-0-6-centos ~]# docker pull hello-world Using default tag: latest latest: Pul…

微服务架构 VS 单体架构

在软件行业&#xff0c;微服务架构是一种重要的发展趋势。这一趋势&#xff0c;不仅仅是对企业内的IT信息系统建设&#xff0c;甚至在企业向数字化转型方面&#xff0c;都有着深远的影响。微服务架构与传统的单体软件架构代表着IT产业处理软件开发方式的一个根本性转变&#xf…

【C++11】异常

&#x1f308;1.C语言传统处理错误的方式 在讲解C的异常机制之前我们先来复习一下传统的处理错误的方式。 传统的错误处理机制&#xff1a; 1.终止程序、如assert , 缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除0错误时就会终止程序。2.返回错误码、缺陷&…