Shiro详解(超全面)

news2024/12/27 14:34:45

目录:

  • 一、简介
  • 二、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框架整合之后的基础上实现的

  1. 导入依赖
<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>
  1. 创建表
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;
  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; //角色的状态
}
  1. UserMapper接口
public interface UserMapper {
    //根据用户账号查询用户信息
    User queryByUsername(String username);
}
  1. 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>
  1. UserService接口
public interface UserService {
    //使用用户名密码登录
    Map<String, Object> login(String username, String password);
    //将已登录的用户登出
    Map<String, Object> logout();
}
  1. 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;
    }
}
  1. 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();
    }

}
  1. 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());
    }
}
  1. 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>
  1. 把shiro配置文件导入applicationContext中,让spring管理
<import resource="spring-shiro.xml"/>
  1. 配置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>
  1. 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、认证流程

shiro认证流程图
  1. 创建token令牌,token中有用户提交的认证信息即账号和密码;

  2. 执行subject.login(token),最终由securityManager通过Authenticator进行认证;

  3. Authenticator的实现ModularRealmAuthenticator调用MyShiroRealm以获取系统中用户真实的账号和密码;

  4. MyShiroRealm先根据token中的账号去系统中找该账号,如果找不到则抛出UnknownAccountException异常。如果找到则返回AuthenticationInfo(含真实密码);

  5. Authenticator根据realm返回的AuthenticationInfo匹配密码,失败则抛出IncorrectCredentialsException异常,成功则认证通过。

2、常见异常

  1. UnknownAccountException 账号不存在异常
org.apache.shiro.authc.UnknownAccountException: No account found for user...
  1. IncorrectCredentialsException 输入密码错误异常
org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.
  1. AuthenticationException 认证异常
    1. DisabledAccountException 帐号被禁用
    2. LockedAccountException 帐号被锁定
    3. ExcessiveAttemptsException 登录失败次数过多
    4. ExpiredCredentialsException 凭证过期

五、授权流程

1、授权流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eY7hr3cg-1682513591073)(G:\桌面\备课\上课\ssm\课件图\shiro授权流程.png)]

  1. 创建Security Manager
  2. 主体subject授权
  3. 主体授权是交给Security Manager授权
  4. Security Manager调用授权器Authorizer授权
  5. 通过Realm在数据库或者缓存中来获取授权的数据(角色数据和权限数据)

2、自定义realm实现授权

​ 上述入门案例中,我们实现了使用自定义realm实现认证过程,接下来,我们完善使用自定义realm实现授权

  1. 修改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>
  1. 修改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 案例

  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>
  1. shiro配置文件中,过滤链放行shiroTag.jsp页面
/jsp/shiroTag.jsp = anon

七、加密

1、散列算法

​ 散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111和salt(盐,一个随机数)进行散列,这样虽然密码都是111111加不同的盐会生成不同的散列值。

2、加密工具类

  1. 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();
    }
}
  1. 测试
@Test
public void applyMd5(){
    String password = "111";
    String slat = "admin";
    String hashedPassword = MD5Util.md5(password, slat);
    System.out.println(hashedPassword);
}
// 输出607a79936b28c5ddfe26940cf5980585

3、认证加密

  1. 修改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>
  1. 修改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());
}

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

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

相关文章

JavaScript日期库之date-fn.js

用官网的话来说&#xff0c;date-fn.js 就是一个现代 JavaScript 日期实用程序库&#xff0c;date-fns 为在浏览器和 Node.js 中操作 JavaScript 日期提供了最全面、但最简单和一致的工具集。那实际用起来像它说的那么神奇呢&#xff0c;下面就一起来看看吧。 安装 安装的话就…

深度学习 -- pytorch 计算图与动态图机制 autograd与逻辑回归模型

前言 pytorch中的动态图机制是pytorch这门框架的优势所在&#xff0c;阅读本篇博客可以使我们对动态图机制以及静态图机制有更直观的理解&#xff0c;同时在博客的后半部分有关于逻辑回归的知识点&#xff0c;并且使用pytorch中张量以及张量的自动求导进行构建逻辑回归模型。 …

Springboot 自动装配流程分析

目录 1.基础知识&#xff1a; 2.具体代码执行流程 3.流程总结&#xff1a; 4.参考文章&#xff1a; 1.基础知识&#xff1a; springboot的自动装配是利用了spring IOC容器创建过程中的增强功能&#xff0c;即BeanFactoryPostProcessor&#xff0c; 其中的ConfigurationCla…

【JavaEE】SpringBoot的日志

目录 日志作用 SpringBoot日志框架 日志打印 日志级别 类型 作用 修改级别 日志永久化 配置日志文件目录 配置日志文件名 简化日志打印和永久化——lombok 日志作用 问题定位&#xff1a;可以帮助开发人员快速找到问题出现的位置系统监控&#xff1a;可以把系统的运…

你不知道的node.js小知识——使用nvm管理node版本及node与npm版本对应关系详解

一、下载和安装nvm管理包 &#xff08;1&#xff09;下载链接 https://github.com/coreybutler/nvm-windows/releases (我选的是nvm-setup.exe) &#xff08;2&#xff09;解压安装 &#xff08;2次选择文件要安装的目录 第一次是nvm 第二次是node.js&#xff09; &#xff08;…

01.DolphinScheduler集群搭建

文章目录 关于Apache DolphinScheduler简介特性简单易用丰富的使用场景High ReliabilityHigh Scalability 软硬件环境建议配置1. Linux 操作系统版本要求2. 服务器建议配置生产环境 3. 网络要求4. 客户端 Web 浏览器要求 官网地址 单机部署(没啥用)前置准备工作启动 DolphinSch…

J - Playing in a Casino

题意&#xff1a;相当于比大小的赌博计算赌徒一共需要支出多少赌资 比大小的规则很简单&#xff0c;是 在这个游戏中&#xff0c;有一个套牌由n卡。每张卡都有m数字写在上面。每个n玩家从一副牌中只收到一张牌。 然后所有玩家成对玩&#xff0c;每对玩家只玩一次。因此&#x…

SpringBoot 中 4 种常用的数据库访问方式

SpringBoot 中常用的数据库访问方式主要有以下几种&#xff1a; SpringBoot 是一个非常流行的 Java 开发框架&#xff0c;它提供了大量的开箱即用的功能&#xff0c;包括数据库访问。在开发过程中&#xff0c;我们经常需要使用数据库&#xff0c;因此选择一种合适的数据库访问…

Day2_vue集成elementUI完善布局

上一节&#xff0c;实现了从O到vue页面主体框架的搭建&#xff0c;这一节补充完善搜索框&#xff1b;新增、删除、导入、导出等按钮&#xff1b;表格设置&#xff1b;分页&#xff1b;面包屑的实现&#xff01; 目录 搜索框 新增删除、导入、导出按钮 表格设置 设置边框&a…

记录安装Nodejs和HBuilderX搭建、部署微信小程序开发环境(一)

文章目录 1 前言2 注册小程序账号3 安装微信开发者工具4 安装Nodejs和HBuilderX4.1 windows用户安装Nodejs4.2 macos/linux用户安装Nodejs4.3 安装HBuilder X 5 创建项目5.1 新建一个项目5.2 进行基本配置 6 HBuilderX同步微信开发者工具6.1 打开服务端口6.2 调用微信开发者工具…

PHP初识

php简介 PHP全称超文本预处理语言&#xff0c;是在服务器端执行的脚本语言&#xff0c;是一种简单的&#xff0c;面向对象的开源脚本语言PHP脚本可以让Web开发人员快速的书写动态生成的网页 PHP脚本以<?php开始&#xff0c;以?>结束 <?php echo "hello world&…

Visual Studio调试代码教学

本篇博客主要讲解程序员最应该掌握的技能之一——调试。我个人认为&#xff0c;学习编程&#xff0c;有2件事情非常重要&#xff0c;一是画图&#xff0c;一是调试。下面我会以Visual Studio 2022为例&#xff08;VS的其他版本大同小异&#xff09;&#xff0c;演示如何调试一个…

测试开发实战项目 | 搭建Pytest接口自动化框架

一、预研背景 目前系统研发多为前后端分离&#xff0c;当后端接口研发完成后&#xff0c;可以不依赖前端界面通过接口测试提前发现问题并解决。同时由于软件迭代周期不断缩短&#xff0c;开发新功能后又担心影响原有功能&#xff0c;可以通过接口自动化进行原有功能快速回归测…

spi,iic,uart,pcie区别

一、spi SPI 是英语Serial Peripheral interface的缩写&#xff0c;顾名思义就是串行外围设备接口&#xff0c;是同步传输协议&#xff0c;特征是&#xff1a;设备有主机&#xff08;master&#xff09;和从机&#xff08;slave&#xff09;的区分&#xff0c;主机在通讯时发送…

分治与减治算法实验: 排序中减治法的程序设计

目录 前言 实验内容 实验目的 实验分析 实验过程 流程演示 写出伪代码 实验代码 代码详解 运行结果 总结 前言 本文介绍了算法实验排序中减治法的程序设计。减治法是一种常用的算法设计技术&#xff0c;它通过减少问题的规模来求解问题。减治法可以应用于排序问题&…

mysql数据库自动备份

前言 服务器中数据库的数据是最重要的东西,如果因为某些情况导致数据库数据错误,数据错乱或数据库崩溃,这时一定要及时的修复,但如果数据丢失或数据没法用了,这时就要回滚数据了,而这时就需要我们经常的备份数据库的数据 正文 一般别人都会推荐使用Navicat来备份和连接数据库…

Kafka时间轮(TimerWheel)--算法简介

一、简介 一个简单的时间轮是一个定时器任务桶的循环列表。 让u作为时间单位。尺寸为n的时间轮有n个桶&#xff0c;可以在n*u的时间间隔内保存定时器任务。每个bucket保存属于相应时间范围的计时器任务。 在开始时&#xff0c; 第一个桶保存[0&#xff0c;u&#xff09;的任务…

第7章 “字典”

1.字典简介 字典是什么&#xff1f; 解答&#xff1a;与集合类似&#xff0c;也是一种存储唯一值的数据结构&#xff0c;但它是以键值对的形式来存储。(键值对是最重要的特性)在Es6中新增了字典&#xff0c;名为Map字典的常用操作&#xff1a;增删改查 const map new Map()/…

使用PY003基于外部中断+定时器的方式实现NEC红外解码

写在前边 最近项目用到一款遥控器是38K红外载波,NEC协议的&#xff0c;找了很多帖子有看到用外部中断下降沿判断&#xff08;但可惜判定数据的方式是while在外部中断里面死等的&#xff09;&#xff0c;有看到用100us定时器定时刷来判断&#xff0c;感觉都不太适合用在我这个工…

基于MATLAB实现WSN(无线传感器网络)的LEACH(低能耗自适应集群层次结构)(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 低能耗自适应集群层次结构&#xff08;“LEACH”&#xff09;是一种基于 TDMA 的 MAC 协议&#xff0c;它与无线传感器网络 &a…