Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01
这个小项目包含了注册与登录,使用了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相关推荐
- Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02
代码延续地址:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01 1.创建t_role角色表(比如管理员admin,普通用户user等),创建t_pers权限表 ...
- Shiro+springboot+mybatis+EhCache(md5+salt+散列)认证与授权-03
从上文:Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02 当每次进行刷新时,都会从数据库重新查询数据进行授权操作,这样无疑给数据库造成很大的压力,所以需要引入 ...
- shiro认证+授权(使用MD5+salt+散列加密)
通过上文自定义realm分析源码可得https://blog.csdn.net/Kevinnsm/article/details/11183124 用户认证在doGetAuthenticationIn ...
- shiro使用md5salt哈希散列加密
我们先得到MD5加密后的字串 @Testpublic void test01(){//md5Md5Hash md5Hash = new Md5Hash("123456");Syst ...
- 【计算机网络】网络安全 : 报文鉴别 ( 密码散列函数 | 报文摘要算法 MD5 | 安全散列算法 SHA-1 | MAC 报文鉴别码 )
文章目录 一.报文鉴别 二.鉴别分类 三.报文鉴别 四.密码散列函数 五.MD5 算法 六.SHA-1 安全散列算法 七.MAC 报文鉴别码 一.报文鉴别 计算机网络安全措施 : ① 针对被动攻击 ( ...
- shiro的通过md5+salt+hash散列进行注册操作
一 . 注册流程 1.1 描述 通过http://localhost:8888/shiro/user/register访问 进行注册,调用register方法:通过md5+salt+hash(1024 ...
- MD5单向散列算法详解
历史: MD5 叫信息-摘要算法,是一种密码的算法,它可以对任何文件产生一个唯一的MD5验证码,每个文件的MD5码就如同每个人的指纹一样,都是不同的,这样,一旦这个文件在传输过程中,其内容被损坏或者被 ...
- Shiro 几种Realm的使用,认证、授权
目录 Shiro的依赖 IniRealm JdbcRealm 自定义Realm 使用示例 常用的Realm有3种 IniRealm:从ini文件加载安全数据 JdbcRealm:从数据库加载安全数据 ...
- 数据结构(六)散列查找 —— 编程作业01 :电话聊天狂人
数据结构系列内容的学习目录→\rightarrow→浙大版数据结构学习系列内容汇总. 题目描述: 给定大量手机用户通话记录,找出其中通话次数最多的聊天狂人. 输入格式: 输入首先给出正整数N( ...
最新文章
- 如何优雅的关闭容器,看这一篇就够了
- 嵌入式linux设计报告,嵌入式linux课程设计报告
- sound.js # pixi辅助插件 — 中文翻译教程
- Apollo分布式配置中心踩坑
- numpy 中对axis参数的理解
- ML《决策树(二)C4.5》
- windows 下安装 elasticsearch 以及 head 管理插件
- 第二章 身份验证——跟我学习springmvc shiro mybatis
- 与大家分享学钢琴会出现的一些问题及解决方法
- 哪种pdf编辑器更方便使用
- 从头配置一台医学影像处理的电脑 Ubuntu20.04
- 计算机上没有端口DOT4,hp1010win7系统打印机装不了dot4端口没反应
- uniapp js 金额与星星**符号互转
- 基于视频的人体姿态检测
- Beaglebone Black–智能家居控制系统 LAS - 用 UART 连接 ESP8266 (ESP-01 版)
- 【博客438】Kubernetes IPAM分配IP原理
- php touppercase strtoupper,php大小写转换函数(strtolower、strtoupper)用法介绍
- 计算机系统如何重置,如何重置电脑系统?看这里1分钟教会你!
- 健身运动燃烧脂肪的三个必要条件
- 用友U8其中一个账套提示演示期已到期-修复方法
热门文章
- 【web】Good ad ~
- java 队列的数组_JAVA-循环数组实现简单的队列
- mysql开启perform sch_MySQL Performance schema设置的一些建议选项
- 切换ip下的sql server用户权限丢失_Zabbix_server高可用之文件同步
- vue dplayer 加载失败_最新vue脚手架项目搭建,并解决一些折腾人的问题
- Maven_1.了解Maven以及其安装配置
- oracle_base,Oracle--基础知识--Oracle 数据库目录 ORACLE_BASE ORACLE_HOME
- flask mysql 版本_Flask mysql
- python爬取app中的音频_Python爬取抖音APP,只需要十行代码
- android python 纠正图片,Python脚本替换Android资源(包名,图片,文件内容)