目录:
- 一、简介
- 二、Shiro的整体架构
- 1、Subject
- 2、Security Manager
- 3、Cryptography
- 4、Authenticator
- 5、Authorizer
- 6、realm
- 7、sessionManager
- 8、SessionDAO
- 9、CacheManager
- 三、入门案例
- 四、认证流程
- 1、认证流程
- 2、常见异常
- 五、授权流程
- 1、授权流程
- 2、自定义realm实现授权
- 六、shiro标签(JSP)
- 1、标签
- 1.1 案例
- 七、加密
- 1、散列算法
- 2、加密工具类
- 3、认证加密
一、简介
- Shiro安全框架是Apache提供的一个强大灵活的安全框架
- Shiro安全框架提供了认证、授权、企业会话管理、加密、缓存管理相关的功能,使用Shiro可以非常方便的完成项目的权限管理模块开发
- 简单灵活、可脱离spring
二、Shiro的整体架构
1、Subject
Subject即主体(可以把当前用户理解为主体),外部应用与Subject进行交互,Subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在Shiro中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过Subject进行认证授,而Subject是通过SecurityManager安全管理器进行认证授权
2、Security Manager
SecurityManager即安全管理器,对全部的Subject进行安全管理,它是Shiro的核心,负责对所有的Subject进行安全管理。通过SecurityManager可以完成Subject的认证、授权等,实质上SecurityManager是通过**Authenticator进行认证,通过Authorizer**进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
3、Cryptography
Cryptography即密码管理,Shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
4、Authenticator
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,Shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
5、Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
6、realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库,那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
7、sessionManager
sessionManager即会话管理,Shiro框架定义了一套会话管理,它不依赖web容器的session,所以Shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
8、SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
9、CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
三、入门案例
该案例是基于SSM框架整合之后的基础上实现的
- 导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
- 创建表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限id,主键',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名称',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限访问的url资源',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户管理', '/sys');
INSERT INTO `permission` VALUES (2, '教师管理', '/teacher');
INSERT INTO `permission` VALUES (3, '学生管理', '/student');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
`role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名',
`state` tinyint(4) NULL DEFAULT 1 COMMENT '状态,1可用,0不可用',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '管理员', 1);
INSERT INTO `role` VALUES (2, '普通用户', 1);
-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`role_id` int(11) NOT NULL COMMENT '关联role表的角色id',
`permission_id` int(11) NOT NULL COMMENT '关联permission表的权限id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1, 1);
INSERT INTO `role_permission` VALUES (2, 1, 2);
INSERT INTO `role_permission` VALUES (3, 1, 3);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户Id,主键',
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户账号',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', '111');
INSERT INTO `user` VALUES (2, 'zhangsan', '111');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` int(11) NOT NULL COMMENT '关联user表的用户id',
`role_id` int(11) NOT NULL COMMENT '关联role表的角色id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;
- 创建实体类User和Role
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
private List<Role> roles; //该用户所具有的所有角色
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private int id; //角色表的主键
private String roleName; //角色的名称
private int state; //角色的状态
}
- UserMapper接口
public interface UserMapper {
//根据用户账号查询用户信息
User queryByUsername(String username);
}
- 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.hqyj.cl.mapper.UserMapper">
<!-- 配置映射 -->
<resultMap id="userMap" type="user">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<collection property="roles" ofType="role">
<id column="id" property="id" />
<result column="role_name" property="roleName" />
<result column="state" property="state" />
</collection>
</resultMap>
<!-- 根据用户账号查询用户信息-->
<select id="queryByUsername" resultMap="userMap">
SELECT
u.id,
u.username,
u.password,
r.id,
r.role_name,
r.state
FROM
USER u
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON r.id = ur.role_id
WHERE
u.username = #{username}
</select>
</mapper>
- UserService接口
public interface UserService {
//使用用户名密码登录
Map<String, Object> login(String username, String password);
//将已登录的用户登出
Map<String, Object> logout();
}
- UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
//使用用户名密码登录
@Override
public Map<String, Object> login(String username, String password) {
Map<String, Object> result = new HashMap<>();
//获取主体, 代表当前用户的对象
Subject subject = SecurityUtils.getSubject();
//判断当前用户是否已经登录过
if(!subject.isAuthenticated()){
//没认证用户才登录
//使用待认证的用户名和密码创建安全令牌对象
AuthenticationToken token = new UsernamePasswordToken(username, password);
//让shiro框架检查令牌,执行登录
try{
subject.login(token);
}catch (UnknownAccountException e){
//用户不存在
result.put("code", -1);
result.put("message", username + "不存在");
return result;
}catch (IncorrectCredentialsException e){
//用户名和密码不匹配
result.put("code", -2);
result.put("message", username + "密码错误");
return result;
}catch (AuthenticationException e){
//其他任何异常
e.printStackTrace();
result.put("code", -3);
result.put("message", username + "认证失败");
return result;
}
}
//已经通过认证
System.out.println(username + "认证通过了....");
//从shiro提供session对象中获取已经认证成功的用户信息
Object user = subject.getSession().getAttribute("user");
result.put("code", 200);
result.put("message", username +"登录成功");
result.put("loginUser", user);
return result;
}
// 将已登录的用户登出
@Override
public Map<String, Object> logout() {
//获取当前用户
Subject subject = SecurityUtils.getSubject();
Object username = subject.getPrincipal();
//执行登出 , 即删除所有已经登录的相关信息
subject.logout();
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("message", username + "用户登出成功");
return result;
}
}
- UserController
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//用户登录
@RequestMapping("/login")
@ResponseBody //将返回的Map对象转换成json格式字符串,直接放在响应体中
public Map<String, Object> login(User user){
//1、对请求参数做判空
if(user == null){
Map<String, Object> result = new HashMap<>();
result.put("code",-20);
result.put("message","请求参数异常");
return result;
}
//2、调用业务层执行业务,获取结果,然后直接返回数据
return userService.login(user.getUsername(), user.getPassword());
}
//用户登出
@RequestMapping("/logout")
@ResponseBody
public Map<String, Object> logout(){
//调用业务层实现业务
return userService.logout();
}
}
- MyShiroRealm
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
//获取授权信息,由开发者提供shiro框架已认证过用户的权限信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//获取认证信息
//由开发者来编写,实现从数据库中查询待认证的用户的用户信息。以提供給shiro框架进行密码匹配工作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//使用token对象获取待登录用户的用户名
String username = (String)token.getPrincipal();
//从数据库中查询该username的用户的信息
User user = userMapper.queryByUsername(username);
//判断待登录用户是否存在
if(user == null){
throw new UnknownAccountException(username + "不存在");
}
//保存用户信息
//获取shiro提供的当前用户的会话对象
Session session = SecurityUtils.getSubject().getSession();
//在shiro提供的会话对象中共享数据
session.setAttribute("user", user);
//创建一个SimpleAuthenticationInfo对象
//三个参数: 1、用户名,2、数据库中用户的密码 3、realm的名称
//返回AuthenticationInfo对象
return new SimpleAuthenticationInfo(
user.getUsername(),user.getPassword(),getName());
}
}
- shiro配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置我们自定义realm类 bean-->
<bean id="myShiroRealm" class="com.hqyj.cl.utils.MyShiroRealm">
</bean>
<!-- 配置shiro的核心对象 安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--依赖注入我们自已定义realm bean -->
<property name="realm" ref="myShiroRealm" />
</bean>
<!-- 配置shiro的过滤器bean 执行授权检查相关功能,这里得先配上 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
- 把shiro配置文件导入applicationContext中,让spring管理
<import resource="spring-shiro.xml"/>
- 配置shiro过滤器 web.xml
<!-- 配置代理过滤器, 由spring提供实现,代理的目标对象是在ioc容器中的真实过滤器bean
filter-name标签指定被代理的bean,这里的shiroFilter就是在ioc容器中的shiro过滤器bean的id
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- login.jsp
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>后台登录</title>
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="shortcut icon" href="<%=basePath%>static/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="<%=basePath%>static/css/font.css">
<link rel="stylesheet" href="<%=basePath%>static/css/xadmin.css">
<script type="text/javascript" src="<%=basePath%>static/js/jquery-2.1.1.min.js"></script>
<script src="<%=basePath%>static/lib/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="<%=basePath%>static/js/xadmin.js"></script>
</head>
<body class="login-bg">
<div class="login">
<div class="message">用户登录</div>
<div id="darkbannerwrap"></div>
<hr class="hr15">
<form action="" method="post" class="layui-form">
<input name="username" placeholder="用户名" id="username" type="text" class="layui-input" >
<hr class="hr15">
<input name="password" placeholder="密码" id="password" type="password" class="layui-input">
<hr class="hr15">
<input value="登录" style="width:100%;" type="button" οnclick="login()">
<hr class="hr20" >
</form>
<script>
function login() {
console.log()
$.ajax({
url:"../user/login",
dataType:"json",
type:"post",
data:{
"username":$("#username").val(),
"password":$("#password").val(),
},success:function (data) {
console.log(data)
},error:function () {
alert("服务器错误");
}
})
}
</script>
</div>
</body>
</html>
四、认证流程
1、认证流程
-
创建token令牌,token中有用户提交的认证信息即账号和密码;
-
执行subject.login(token),最终由securityManager通过Authenticator进行认证;
-
Authenticator的实现ModularRealmAuthenticator调用MyShiroRealm以获取系统中用户真实的账号和密码;
-
MyShiroRealm先根据token中的账号去系统中找该账号,如果找不到则抛出UnknownAccountException异常。如果找到则返回AuthenticationInfo(含真实密码);
-
Authenticator根据realm返回的AuthenticationInfo匹配密码,失败则抛出IncorrectCredentialsException异常,成功则认证通过。
2、常见异常
- UnknownAccountException 账号不存在异常
org.apache.shiro.authc.UnknownAccountException: No account found for user...
- IncorrectCredentialsException 输入密码错误异常
org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.
- AuthenticationException 认证异常
- DisabledAccountException 帐号被禁用
- LockedAccountException 帐号被锁定
- ExcessiveAttemptsException 登录失败次数过多
- ExpiredCredentialsException 凭证过期
五、授权流程
1、授权流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eY7hr3cg-1682513591073)(G:\桌面\备课\上课\ssm\课件图\shiro授权流程.png)]
- 创建Security Manager
- 主体subject授权
- 主体授权是交给Security Manager授权
- Security Manager调用授权器Authorizer授权
- 通过Realm在数据库或者缓存中来获取授权的数据(角色数据和权限数据)
2、自定义realm实现授权
上述入门案例中,我们实现了使用自定义realm实现认证过程,接下来,我们完善使用自定义realm实现授权
- 修改shiro的配置文件
<!-- 配置shiro的过滤器bean 执行授权检查相关功能,这里得先配上 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 配置shiro的过滤器bean 执行授权检查,
敏感资源: web项目中就是一系列能访问操作数据库的url
浏览器访问敏感资源,shiro过滤器拦截这次请求,执行授权检查,
通过授权域realm对象来获取当前用户所具有的权限。
哪些是敏感资源以及需要的身份,需要在此处定义 -->
<!-- loginUrl配置:如果没有认证的用户访问敏感资源,将被shiro过滤器强制跳转到指定url资源 -->
<property name="loginUrl" value="/sys/goLogin"/>
<!-- 已认证的用户访问需要特定权限的敏感资源时,如果没有该特定权限时,将被shiro过滤器强制跳转到指定url资源 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<!-- 敏感资源及用户身份的定义-->
<property name="filterChainDefinitions">
<value>
<!-- 过滤链的语法: 敏感资源的url = 身份 (一行写一个配置)
身份: anon 匿名用户(未认证的用户)
authc 已通过认证的用户
user 曾经认证过的用户
roles[角色名] 拥有某个角色的用户
perms[权限名] 拥有某个权限的用户
-->
/sys/goLogin = anon <!-- 匿名用户才能访问 -->
/user/login = anon
/user/logout = authc <!-- 已认证的用户可以访问-->
/sys/goMyself = roles[普通用户] <!-- 普通用户才能访问 -->
/sys/goIndex = roles[管理员] <!-- 管理员才能访问 -->
/static/** = anon
/* = authc <!-- /** 根路径下的所有url包括子路径 /* 根路径下的一级中所有的url -->
<!-- 授权时shiro过滤器会从上往下搜索过滤器链,找到一个url匹配就结束。
统配的资源一般写在靠下面位置
-->
</value>
</property>
</bean>
- 修改MyShiroRealm中授权方法
//获取授权信息,由开发者提供shiro框架已认证过用户的权限信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("shiro来找我们获取授权信息了");
//获取当前已认证(待授权用户)的用户的账号
String username = (String)principals.getPrimaryPrincipal();
// 查询该用户的角色或权限
User user = userMapper.queryByUsername(username);
Set<String> roles = new HashSet<>();
if(user.getRoles() != null){
//遍历用户的角色信息
for(Role one:user.getRoles()){
//角色名添加到role集合中
roles.add(one.getRoleName());
}
}
//查询用户的权限信息,留给同学们后续实现
Set<String> perms = new HashSet<>();
//创建授权信息对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//设置角色信息
info.setRoles(roles);
//设置权限信息
info.setStringPermissions(perms);
//返回授权信息对象
return info;
}
六、shiro标签(JSP)
shiro提供一系列jsp标签,使用该标签可以非常方便在jsp页面中根据用户的授权信息来控制菜单、按钮等ui元素的显示。
1、标签
标签名称 | 标签条件(均是显示标签内容) |
---|---|
<shiro:authenticated> | 认证通过用户 |
<shiro:notAuthenticated> | 未认证通过用户 |
<shiro:guest> | 用户未认证时 |
<shiro:user> | 认证通过或已记住的用户时 |
<shiro:hasAnyRoles name=“abc,123” > | 在有abc或者123角色时 |
<shiro:hasRole name=“abc”> | 拥有角色abc |
<shiro:lacksRole name=“abc”> | 没有角色abc |
<shiro:hasPermission name=“abc”> | 拥有权限资源abc |
<shiro:lacksPermission name=“abc”> | 没有abc权限资源 |
<shiro:principal property=“username”> | 显示用户身份名称 |
1.1 案例
- 创建shiroTag.jsp页面
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>shiroTag.jsp</title>
</head>
<body>
<!-- 根据当前用户身份,来决定是否展示标签体中的内容 -->
<shiro:guest>匿名用户可以看到</shiro:guest> <br>
<shiro:authenticated>认证过的用户可以看到</shiro:authenticated>
<shiro:hasAnyRoles name="管理员,普通用户">管理员或普通用户可以看到</shiro:hasAnyRoles>
<shiro:hasRole name="管理员">管理员才能看到</shiro:hasRole>
<shiro:hasRole name="普通用户">普通用户才能看到</shiro:hasRole>
</body>
</html>
- shiro配置文件中,过滤链放行shiroTag.jsp页面
/jsp/shiroTag.jsp = anon
七、加密
1、散列算法
散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111和salt(盐,一个随机数)进行散列,这样虽然密码都是111111加不同的盐会生成不同的散列值。
2、加密工具类
- MD5Util加密工具类
public class MD5Util {
public static String md5(String password, String salt){
/*
algorithmName代表进行加密的算法名称、
source代表需要加密的元数据,如密码、
salt代表盐,需要加进一起加密的数据、
hashIterations代表hash迭代次数。
* */
return new SimpleHash("MD5", password, salt,1024).toString();
}
}
- 测试
@Test
public void applyMd5(){
String password = "111";
String slat = "admin";
String hashedPassword = MD5Util.md5(password, slat);
System.out.println(hashedPassword);
}
// 输出607a79936b28c5ddfe26940cf5980585
3、认证加密
- 修改shiro配置文件
<!-- 配置密码匹配器 散列的凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 指定散列算法的细节 这里定义的算法和迭代次数需要和加密工具类定义的一致-->
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="1024"/>
</bean>
<!-- 配置我们自定义realm类 bean-->
<bean id="myShiroRealm" class="com.hqyj.cl.utils.MyShiroRealm">
<!-- 依赖注入我们定义的凭证匹配器,bean-->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
- 修改MyShiroRealm认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//使用token对象获取待登录用户的用户名
String username = (String)token.getPrincipal();
//从数据库中查询该username的用户的信息
User user = userMapper.queryByUsername(username);
System.out.println(user + "#AuthenticationInfo");
//判断待登录用户是否存在
if(user == null){
throw new UnknownAccountException(username + "不存在");
}
//保存用户信息
//获取shiro提供的当前用户的会话对象
Session session = SecurityUtils.getSubject().getSession();
//在shiro提供的会话对象中共享数据
session.setAttribute("user", user);
//创建一个SimpleAuthenticationInfo对象
//三个参数: 1、用户名,2、数据库中用户的密码 3、realm的名称
/* 使用4参数构造方法构造SimpleAuthenticationInfo对象
SimpleAuthenticationInfo(Object principal,
Object hashedCredentials,
ByteSource credentialsSalt,
String realmName)
其中第二个参数:Object hashedCredentials 为散列过的用户密码
其中第三个参数:ByteSource credentialsSalt 即为对用户密码散列时用到的salt,
这里作为盐的字符串采用用户的账号名,与加密时使用的salt要一致才能认证成功
*/
ByteSource salt = ByteSource.Util.bytes(user.getUsername());
//返回AuthenticationInfo对象
return new SimpleAuthenticationInfo(user.getUsername(),
user.getPassword(), salt, getName());
}