这个小项目包含了注册与登录,使用了springboot+mybatis+shiro的技术栈;当用户在浏览器登录时发起请求时,首先这一系列的请求会被拦截器进行拦截(ShiroFilter),然后拦截器根据用户名去数据库寻找是否有相对应的user实体;如果有则返回封装到User类中(没有就用户名错误),然后比对密码是否一致;如果都通过了则认证成功;登录到主页面;然后主页面有不同的功能,不同的用户拥有不同的权限,有的能看到,有的则无法看到;然后如果不登陆直接访问主页面,会被强制跳转到登录页面;另外登录之后也可以退出登录;

通过分析上文:https://blog.csdn.net/Kevinnsm/article/details/11187650

1.首先快速创建一个springboot项目(使用spring向导)

<?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.4.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.hao</groupId><artifactId>springboot-shiro</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-shiro</name><description>Demo project for Spring Boot</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>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></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.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.7.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.19</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></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>

2.数据库建表

这里的salt字段主要是为了使用md5+salt+散列加密时,需要保存salt字符串,以便在业务层比较密码正确与否时一致

3.注册、登录和主页面

<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><h1>用户注册</h1><form action="${pageContext.request.contextPath}/user/register" method="post">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br><input type="submit" value="注册"></form>
</body>
</html>
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><h1>用户登录</h1><form action="${pageContext.request.contextPath}/user/login" method="post">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br><input type="submit" value="登录"></form>
</body>
</html>
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><h1>系统主页,欢迎你的到来</h1><a href="${pageContext.request.contextPath}/user/outLogin">退出</a><ul><shiro:hasAnyRoles name="user"><li><a href="">用户管理</a><ul><shiro:hasPermission name="user:add:*"><li><a href="">添加用户</a></li></shiro:hasPermission><li><a href="">删除用户</a></li><li><a href="">修改用户</a></li><li><a href="">查询用户</a></li></ul></li></shiro:hasAnyRoles><shiro:hasRole name="admin"><li><a href="">商品管理</a> </li><li><a href="">订单管理</a> </li><li><a href="">物流管理</a> </li></shiro:hasRole><shiro:hasRole name="shper"><li><a href="">终极格式化</a> </li></shiro:hasRole></ul>
</body>
</html>

4.配置文件

spring.application.name=shiro
server.servlet.context-path=/shirospring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=hao20001010mybatis.type-aliases-package=com.hao.springboot.entity
mybatis.mapper-locations=classpath:mapper/*.xmllogging.level.com.hao.springboot.dao=debug

5.编写mapper.xml映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hao.springboot.dao.UserDao"><insert id="save" parameterType="User">insert into t_user values(#{id},#{username},#{password},#{salt})</insert><select id="findByUserName" resultType="User">select * from t_user where username=#{username}</select>
</mapper>

6.DAO层

@Repository
public interface UserDao {void save(User user);User findByUserName(String username);
}

7.service层

public interface UserService {void register(User user);User findByUserName(String username);
}
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {@AutowiredUserDao userDao;@Overridepublic void register(User user) {//明文密码进行md5+salt+随机散列//1.生成随机盐String salt = SaltUtil.getSalt(8);//2.将随机盐保存到数据库中user.setSalt(salt);Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);user.setPassword(md5Hash.toHex());userDao.save(user);}@Overridepublic User findByUserName(String username) {return userDao.findByUserName(username);}
}

8.controller层

/*** @author:抱着鱼睡觉的喵喵* @date:2020/12/29* @description:*/
@Controller
@RequestMapping("/user")
public class UserController {@AutowiredUserService userService;/*** 处理身份认证* @param username* @param password* @return*/@RequestMapping("/login")public String login(@RequestParam("username")String username,@RequestParam("password")String password){//获取主体对象Subject subject = SecurityUtils.getSubject();try {subject.login(new UsernamePasswordToken(username,password));return "redirect:/index.jsp";}catch (UnknownAccountException e){e.printStackTrace();System.out.println("用户名错误");}catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("密码错误");}return "redirect:/login.jsp";}/*** 退出用户* @return*/@RequestMapping("/outLogin")public String outLogin(){Subject subject = SecurityUtils.getSubject();subject.logout();           //退出用户return "redirect:/login.jsp";}/*** 用户注册* @return*/@RequestMapping("/register")public String register(User user){try {userService.register(user);return "redirect:/login.jsp";}catch (Exception e){e.printStackTrace();return "redirect:/register.jsp";}}
}

9.User实体类

@Data
@ToString
@AllArgsConstructor
@Accessors(chain = true)
@NoArgsConstructor
public class User {private String id;private String username;private String password;private String salt;}

10.创建ShiroFilter拦截器(拦截所有请求)

import com.hao.springboot.shiro.realm.CustomerRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;/*** @author:抱着鱼睡觉的喵喵* @date:2020/12/29* @description:*/
@Configuration
public class ShiroConfig {/*** 1.创建ShiroFilter* 负责拦截所有请求* @return shiroFilterFactoryBean*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactory(DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//给filter设置安全管理器shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);//配置系统首先资源//配置系统公共资源Map<String,String> map=new HashMap<>();map.put("/user/login","anon");       //设置为公共资源,防止登录死循环map.put("/user/register","anon");map.put("/register.jsp","anon");map.put("/**","authc");  //authc 这个资源需要认证和授权shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//默认界面路径shiroFilterFactoryBean.setLoginUrl("/login.jsp");return shiroFilterFactoryBean;}/*** 安全管理器* @return defaultWebSecurityManager*/@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;}/**** @return*/@Bean("realm")public Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm();//修改凭证校验匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();//设置加密算法为md5credentialsMatcher.setHashAlgorithmName("MD5");//设置散列次数credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);return customerRealm;}
}

11.自定义realm

package com.hao.springboot.shiro.realm;import com.hao.springboot.entity.User;
import com.hao.springboot.service.UserService;
import com.hao.springboot.utils.ApplicationContextUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.apache.shiro.util.ByteSource;
import org.springframework.util.ObjectUtils;import java.util.Arrays;/*** @author:抱着鱼睡觉的喵喵* @date:2020/12/29* @description:    自定义realm完成用户认证和授权*/
public class CustomerRealm extends AuthorizingRealm {/*** 用户授权* @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("调用权限认证:"+principalCollection);String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();if ("Ronin".equals(primaryPrincipal)){simpleAuthorizationInfo.addRoles(Arrays.asList("user","admin","shaper"));simpleAuthorizationInfo.addStringPermission("user:add:*");return simpleAuthorizationInfo;}
//        else if("tom".equals(primaryPrincipal)){
//            simpleAuthorizationInfo.addRole("admin");
//            return simpleAuthorizationInfo;
//        }return null;}/*** 用户认证* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {String principal = (String) authenticationToken.getPrincipal();//在工厂中获取业务对象UserService userService = (UserService) ApplicationContextUtils.getBean("userService");User user = userService.findByUserName(principal);if (!ObjectUtils.isEmpty(user)){return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());}return null;}
}

12.编写salt(因为要使用md5+salt+散列对密码加密)

package com.hao.springboot.utils;import java.util.Random;/*** @author:抱着鱼睡觉的喵喵* @date:2020/12/29* @description:*/
public class SaltUtil {/*** 生成salt的静态方法* @param n* @return*/public static String getSalt(int n){char[] chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()".toCharArray();StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < n; i++) {char aChar = chars[new Random().nextInt(chars.length)];stringBuilder.append(aChar);}return stringBuilder.toString();}
//测试一下public static void main(String[] args) {System.out.println(getSalt(8));}
}

13.我们想要在自定义realm中的doGetAuthenticationInfo()方法调用service层,所以需要
使用静态类获取

package com.hao.springboot.utils;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;/*** @author:抱着鱼睡觉的喵喵* @date:2020/12/29* @description:*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context=applicationContext;}//public static Object getBean(String beanName){return context.getBean(beanName);}
}

14,测试

访问aaaa

强制到登录页面

以下是事先的数据


输入Ronin && 123登录

可以看到Ronin用户能够看所有的权限信息
接着点击退出
输入tom && 123

点击登录

可以发现tom用户什么都看不到,因为没有赋给tom用户任何权限


使用代码控制角色权限

@Controller
@RequestMapping("/limit")
public class LimitController {@RequestMapping("/save")@RequiresRoles(value = {"admin","user"})@RequiresPermissions("user:update:*")public String save(){
//        Subject subject = SecurityUtils.getSubject();
//        if (subject.hasRole("admin")) {
//            System.out.println("保存订单!");
//        }else {
//            System.out.println("无权访问");
//        }System.out.println("进入方法");return "redirect:/index.jsp";}
}

我从这个小案例中我学到了什么
1.使用spring框架,所有的bean都是通过容器管理的;想要使用某个组件直接从容器中拿就行了,比如service注入到controller即可;本案例中的sevice层需要被自定义的realm调用,进行密码认证,但是怎么调用就成了问题,不可能new一个吧,所有bean对象都是通过IOC容器管理的;
所以只有使用静态类,将该静态类加入容器中(使用@Component注解)

@Component
public class ApplicationContextUtils implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context=applicationContext;}//public static Object getBean(String beanName){return context.getBean(beanName);}
}

2.复习了ApplicationContext和BeanFactory的区别
3.shiro与springboot的整合关键在于ShiroFilter拦截器的使用


2021一起加油;

Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01相关推荐

  1. Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02

    代码延续地址:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01 1.创建t_role角色表(比如管理员admin,普通用户user等),创建t_pers权限表 ...

  2. Shiro+springboot+mybatis+EhCache(md5+salt+散列)认证与授权-03

    从上文:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02 当每次进行刷新时,都会从数据库重新查询数据进行授权操作,这样无疑给数据库造成很大的压力,所以需要引入 ...

  3. shiro认证+授权(使用MD5+salt+散列加密)

    通过上文自定义realm分析源码可得https://blog.csdn.net/Kevinnsm/article/details/11183124 用户认证在doGetAuthenticationIn ...

  4. shiro使用md5salt哈希散列加密

    我们先得到MD5加密后的字串 @Testpublic void test01(){//md5Md5Hash md5Hash = new Md5Hash("123456");Syst ...

  5. 【计算机网络】网络安全 : 报文鉴别 ( 密码散列函数 | 报文摘要算法 MD5 | 安全散列算法 SHA-1 | MAC 报文鉴别码 )

    文章目录 一.报文鉴别 二.鉴别分类 三.报文鉴别 四.密码散列函数 五.MD5 算法 六.SHA-1 安全散列算法 七.MAC 报文鉴别码 一.报文鉴别 计算机网络安全措施 : ① 针对被动攻击 ( ...

  6. shiro的通过md5+salt+hash散列进行注册操作

    一 . 注册流程 1.1 描述 通过http://localhost:8888/shiro/user/register访问 进行注册,调用register方法:通过md5+salt+hash(1024 ...

  7. MD5单向散列算法详解

    历史: MD5 叫信息-摘要算法,是一种密码的算法,它可以对任何文件产生一个唯一的MD5验证码,每个文件的MD5码就如同每个人的指纹一样,都是不同的,这样,一旦这个文件在传输过程中,其内容被损坏或者被 ...

  8. Shiro 几种Realm的使用,认证、授权

    目录 Shiro的依赖 IniRealm JdbcRealm 自定义Realm 使用示例 常用的Realm有3种 IniRealm:从ini文件加载安全数据 JdbcRealm:从数据库加载安全数据 ...

  9. 数据结构(六)散列查找 —— 编程作业01 :电话聊天狂人

    数据结构系列内容的学习目录→\rightarrow→浙大版数据结构学习系列内容汇总.   题目描述: 给定大量手机用户通话记录,找出其中通话次数最多的聊天狂人.   输入格式: 输入首先给出正整数N( ...

最新文章

  1. 如何优雅的关闭容器,看这一篇就够了
  2. 嵌入式linux设计报告,嵌入式linux课程设计报告
  3. sound.js # pixi辅助插件 — 中文翻译教程
  4. Apollo分布式配置中心踩坑
  5. numpy 中对axis参数的理解
  6. ML《决策树(二)C4.5》
  7. windows 下安装 elasticsearch 以及 head 管理插件
  8. 第二章 身份验证——跟我学习springmvc shiro mybatis
  9. 与大家分享学钢琴会出现的一些问题及解决方法
  10. 哪种pdf编辑器更方便使用
  11. 从头配置一台医学影像处理的电脑 Ubuntu20.04
  12. 计算机上没有端口DOT4,hp1010win7系统打印机装不了dot4端口没反应
  13. uniapp js 金额与星星**符号互转
  14. 基于视频的人体姿态检测
  15. Beaglebone Black–智能家居控制系统 LAS - 用 UART 连接 ESP8266 (ESP-01 版)
  16. 【博客438】Kubernetes IPAM分配IP原理
  17. php touppercase strtoupper,php大小写转换函数(strtolower、strtoupper)用法介绍
  18. 计算机系统如何重置,如何重置电脑系统?看这里1分钟教会你!
  19. 健身运动燃烧脂肪的三个必要条件
  20. 用友U8其中一个账套提示演示期已到期-修复方法

热门文章

  1. 【web】Good ad ~
  2. java 队列的数组_JAVA-循环数组实现简单的队列
  3. mysql开启perform sch_MySQL Performance schema设置的一些建议选项
  4. 切换ip下的sql server用户权限丢失_Zabbix_server高可用之文件同步
  5. vue dplayer 加载失败_最新vue脚手架项目搭建,并解决一些折腾人的问题
  6. Maven_1.了解Maven以及其安装配置
  7. oracle_base,Oracle--基础知识--Oracle 数据库目录 ORACLE_BASE ORACLE_HOME
  8. flask mysql 版本_Flask mysql
  9. python爬取app中的音频_Python爬取抖音APP,只需要十行代码
  10. android python 纠正图片,Python脚本替换Android资源(包名,图片,文件内容)