shiro详细使用

springboot+shiro整合(mybatis就不说了)

springboot使用jsp有个设置以下,不然会提示找不到jsp文件
application.yml

spring:
  mvc:
    view:
      prefix: /
      suffix: .jsp

然后就是idea调整以下设置,我也不太明白图中这个设置具体是干什么的,不过你不这设置就找不到jsp文件,也就是说找jsp文件的时候不会去webapp目录下面去找。
在这里插入图片描述

数据库脚本
  • sys_dept 部门表
  • sys_resource 资源表
  • sys_role 角色表
  • sys_role_resource 角色和资源的中间表
  • sys_user 用户表
  • sys_user_role 角色和用户中间表
/*
 Navicat Premium Data Transfer

 Source Server         : localhost_3306
 Source Server Type    : MySQL
 Source Server Version : 50645
 Source Host           : localhost:3306
 Source Schema         : shiro

 Target Server Type    : MySQL
 Target Server Version : 50645
 File Encoding         : 65001

 Date: 31/08/2022 15:53:07
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_dept
-- ----------------------------
DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '部门名称',
  `parent_id` int(11) DEFAULT NULL COMMENT '上级部门ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for sys_resource
-- ----------------------------
DROP TABLE IF EXISTS `sys_resource`;
CREATE TABLE `sys_resource`  (
  `resource_id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) NOT NULL COMMENT '父资源ID,一级资源ID为0',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '资源名称',
  `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '菜单URL',
  `permission` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
  `type` int(11) NOT NULL COMMENT '类型  -1:根目录  0:目录   1:菜单   2:按钮',
  `icon` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '资源图标',
  `order_num` int(11) DEFAULT 0 COMMENT '排序',
  `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`resource_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 67 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单管理' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_resource
-- ----------------------------
INSERT INTO `sys_resource` VALUES (1, 0, '系统管理', '0/1', '', 'content:sys', 0, 'fa fa-cog', 2, '2019-10-30 00:16:24', '2019-11-06 03:39:43');
INSERT INTO `sys_resource` VALUES (2, 1, '用户管理', '0/1/2', 'sys/user', 'sys:user:menu', 1, '', 1, '2019-10-30 00:16:24', '2019-11-06 03:52:30');
INSERT INTO `sys_resource` VALUES (3, 1, '角色管理', '0/1/3', 'sys/role', 'sys:role:menu', 1, '', 2, '2019-10-30 00:16:24', '2019-11-06 03:52:37');
INSERT INTO `sys_resource` VALUES (4, 1, '资源管理', '0/1/4', 'sys/resource', 'sys:resource:menu', 1, '', 3, '2019-10-30 00:16:24', '2019-11-06 03:52:42');
INSERT INTO `sys_resource` VALUES (15, 2, '查询用户', '0/1/2/15', '', 'sys:user:list', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:56:36');
INSERT INTO `sys_resource` VALUES (16, 2, '新增用户', '0/1/2/16', '', 'sys:user:add', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:49:43');
INSERT INTO `sys_resource` VALUES (17, 2, '修改用户', '0/1/2/17', '', 'sys:user:update', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:49:54');
INSERT INTO `sys_resource` VALUES (18, 2, '删除用户', '0/1/2/18', '', 'sys:user:delete', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:50:00');
INSERT INTO `sys_resource` VALUES (19, 3, '查询角色', '0/1/3/19', '', 'sys:role:list', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:57:02');
INSERT INTO `sys_resource` VALUES (20, 3, '新增角色', '0/1/3/20', '', 'sys:role:add', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:57:11');
INSERT INTO `sys_resource` VALUES (21, 3, '修改角色', '0/1/3/21', '', 'sys:role:update', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:57:17');
INSERT INTO `sys_resource` VALUES (23, 4, '查询资源', '0/1/4/23', '', 'sys:resource:list', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:58:38');
INSERT INTO `sys_resource` VALUES (24, 4, '新增资源', '0/1/4/24', '', 'sys:resource:add', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:58:43');
INSERT INTO `sys_resource` VALUES (25, 4, '修改资源', '0/1/4/25', '', 'sys:resource:update', 2, '', 0, '2019-10-30 00:16:24', '2019-11-05 23:00:39');
INSERT INTO `sys_resource` VALUES (26, 4, '删除资源', '0/1/4/26', '', 'sys:resource:delete', 2, '', 0, '2019-10-30 00:16:24', '2019-11-05 23:00:42');
INSERT INTO `sys_resource` VALUES (31, 1, '部门管理', '0/1/31', 'sys/dept', 'sys:dept:menu', 1, '', 4, '2019-10-30 00:16:24', '2019-11-06 03:52:47');
INSERT INTO `sys_resource` VALUES (32, 31, '查询部门', '0/1/31/32', '', 'sys:dept:list', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:58:55');
INSERT INTO `sys_resource` VALUES (33, 31, '新增部门', '0/1/31/33', '', 'sys:dept:add', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:59:01');
INSERT INTO `sys_resource` VALUES (34, 31, '修改部门', '0/1/31/34', '', 'sys:dept:update', 2, '', 0, '2019-10-30 00:16:24', '2019-11-06 00:59:05');
INSERT INTO `sys_resource` VALUES (35, 31, '删除部门', '0/1/31/35', '', 'sys:dept:delete', 2, '', 0, '2019-10-30 00:16:24', '2019-11-05 11:38:00');
INSERT INTO `sys_resource` VALUES (48, 3, '删除角色', '0/1/3/48', '', 'sys:role:delete', 2, '', 0, '2019-11-04 01:09:36', '2019-11-06 00:57:28');
INSERT INTO `sys_resource` VALUES (56, 2, '分配角色', '0/1/2/56', '', 'sys:user:assign:role', 2, '', 0, '2019-11-06 00:50:26', '2019-11-06 00:56:00');
INSERT INTO `sys_resource` VALUES (57, 3, '分配资源', '0/1/3/57', '', 'sys:role:assign:resource', 2, '', 0, '2019-11-06 00:58:01', '2019-11-06 00:58:01');
INSERT INTO `sys_resource` VALUES (61, 0, '主页', '0/61', '', 'content:main', 0, 'fa fa-home', 1, '2019-11-06 03:10:19', '2019-11-06 03:49:27');
INSERT INTO `sys_resource` VALUES (62, 61, '控制台', '0/61/62', '', 'main:console:menu', 1, '', 1, '2019-11-06 03:10:37', '2019-11-06 03:49:19');
INSERT INTO `sys_resource` VALUES (63, 0, '统计分析', '0/63', '', 'content:report', 0, 'fa fa-bar-chart-o', 3, '2019-11-06 03:18:11', '2019-11-06 03:49:45');
INSERT INTO `sys_resource` VALUES (64, 63, '访客统计', '0/63/64', '', 'report:visitor:menu', 1, '', 1, '2019-11-06 03:20:59', '2019-11-06 03:40:39');
INSERT INTO `sys_resource` VALUES (65, 63, '订单统计', '0/63/65', '', 'report:order:menu', 1, '', 2, '2019-11-06 03:22:15', '2019-11-06 03:40:43');
INSERT INTO `sys_resource` VALUES (66, -1, '根节点', '0', NULL, NULL, -1, NULL, NULL, '2019-11-03 19:07:10', '2019-11-04 16:11:21');

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称',
  `remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
  `dept_id` int(20) DEFAULT NULL COMMENT '部门ID',
  `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 27 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'admin', '系统管理员', NULL, '2019-10-30 01:03:14', '2022-08-31 15:51:53');
INSERT INTO `sys_role` VALUES (2, 'maintenance', '网站运维', NULL, '2019-10-30 01:03:52', '2022-08-31 15:51:57');

-- ----------------------------
-- Table structure for sys_role_resource
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_resource`;
CREATE TABLE `sys_role_resource`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL COMMENT '角色ID',
  `resource_id` int(11) NOT NULL COMMENT '菜单ID',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `role_id`(`role_id`, `resource_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 862 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色与菜单对应关系' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_role_resource
-- ----------------------------
INSERT INTO `sys_role_resource` VALUES (830, 1, 1);
INSERT INTO `sys_role_resource` VALUES (831, 1, 2);
INSERT INTO `sys_role_resource` VALUES (837, 1, 3);
INSERT INTO `sys_role_resource` VALUES (843, 1, 4);
INSERT INTO `sys_role_resource` VALUES (832, 1, 15);
INSERT INTO `sys_role_resource` VALUES (833, 1, 16);
INSERT INTO `sys_role_resource` VALUES (834, 1, 17);
INSERT INTO `sys_role_resource` VALUES (835, 1, 18);
INSERT INTO `sys_role_resource` VALUES (838, 1, 19);
INSERT INTO `sys_role_resource` VALUES (839, 1, 20);
INSERT INTO `sys_role_resource` VALUES (840, 1, 21);
INSERT INTO `sys_role_resource` VALUES (844, 1, 23);
INSERT INTO `sys_role_resource` VALUES (845, 1, 24);
INSERT INTO `sys_role_resource` VALUES (846, 1, 25);
INSERT INTO `sys_role_resource` VALUES (847, 1, 26);
INSERT INTO `sys_role_resource` VALUES (848, 1, 31);
INSERT INTO `sys_role_resource` VALUES (849, 1, 32);
INSERT INTO `sys_role_resource` VALUES (850, 1, 33);
INSERT INTO `sys_role_resource` VALUES (851, 1, 34);
INSERT INTO `sys_role_resource` VALUES (852, 1, 35);
INSERT INTO `sys_role_resource` VALUES (841, 1, 48);
INSERT INTO `sys_role_resource` VALUES (836, 1, 56);
INSERT INTO `sys_role_resource` VALUES (842, 1, 57);
INSERT INTO `sys_role_resource` VALUES (853, 1, 61);
INSERT INTO `sys_role_resource` VALUES (854, 1, 62);
INSERT INTO `sys_role_resource` VALUES (855, 1, 63);
INSERT INTO `sys_role_resource` VALUES (856, 1, 64);
INSERT INTO `sys_role_resource` VALUES (857, 1, 65);
INSERT INTO `sys_role_resource` VALUES (858, 2, 1);
INSERT INTO `sys_role_resource` VALUES (859, 2, 2);
INSERT INTO `sys_role_resource` VALUES (860, 2, 15);
INSERT INTO `sys_role_resource` VALUES (861, 2, 16);

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
  `salt` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '盐',
  `email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '邮箱',
  `mobile` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '手机号',
  `dept_id` int(20) DEFAULT NULL COMMENT '部门ID',
  `status` int(11) NOT NULL DEFAULT 0 COMMENT '状态  0:正常   1:禁用   2:锁定',
  `deleted` int(11) NOT NULL DEFAULT 0 COMMENT '0正常  1删除',
  `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`user_id`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统用户' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', '6f89a35fca3ed82005e9ec4ddabc8226', '98C69A2446384F22B5EFC7874095AC69', 'admin@126.com', '13800000000', NULL, 0, 0, '2019-11-05 23:03:24', '2019-11-05 23:04:16');
INSERT INTO `sys_user` VALUES (2, 'jack', '6f89a35fca3ed82005e9ec4ddabc8226', '98C69A2446384F22B5EFC7874095AC69', 'jack@139.com', '18959139189', 1, 0, 0, '2019-10-30 01:05:10', '2019-10-30 15:26:37');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `user_id` int(20) NOT NULL COMMENT '用户ID',
  `role_id` int(20) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `user_id`(`user_id`, `role_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 37 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户与角色对应关系' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (34, 1, 1);
INSERT INTO `sys_user_role` VALUES (36, 2, 2);

SET FOREIGN_KEY_CHECKS = 1;

pom
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath />
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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>
        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.8.0</version>
        </dependency>
        <!--整合mybatis-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--整合druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!--jsp解析依赖-->
        <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>
    </dependencies>
config

遇到过的坑:

  • 静态文件js明明显示获取成功http的状态是200,但是这个地方一直报:Uncaught SyntaxError: Unexpected token ‘<‘ 这个错误,后面通过一篇文章终于搞懂了,因为设置了权限控制,必须要把静态引用资源放行,不然就会出现这种问题。
  • 使用shiro标签时候@RequiresRoles(“admin”)不起作用,最后查到需要在config里面配置,他这个注解是用的aop。
import com.shiro.test.shiro.ShiroRealm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.apache.shiro.mgt.SecurityManager;

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

@Configuration
public class ShiroConfig {

    /**
     * 负责拦截所有请求
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        /**
         * 添加shiro内置过滤器
         * anon 无需认证就可访问
         * authc 必须认证才能访问
         * user 必须拥有 记住我 功能才能访问
         * perms 拥有对某个资源的权限才能访问
         * role 拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap();
        filterMap.put("/login.jsp","anon");
        filterMap.put("/register.jsp","anon");
        filterMap.put("/user/*","anon");
        filterMap.put("/js/*","anon");
        filterMap.put("/**","authc");
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager  = new DefaultWebSecurityManager();
        //安全管理器关联userRealm
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    /**
     * 身份验证
     * @return
     */
    @Bean
    public ShiroRealm userRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();
        return shiroRealm;
    }

    /**
     *  开启shiro aop注解支持.否则@RequiresRoles等注解无法生效
     *  使用代理方式;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 自动创建代理
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
}

自定义Realm

这个地方密码比对是用的md5加密,数据库不可能存明文,我将盐和加密之后的密码都存到数据库中。我可以根据用户名拿到盐和密文密码,那么我将前端传过来的密码再按相同步骤加密一次,如果两个密文是一样,就说明密码是正确的。

import com.shiro.test.mapper.UserMapper;
import com.shiro.test.model.User;
import com.shiro.test.utils.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

@Slf4j
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    /**
     * 登录认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 1.获取用户输入的用户名
        String username = token.getUsername();
        // 2.获取用户输入的密码
        String password = new String(token.getPassword());
        // 3.根据用户名去DB查询对应的用户信息
        User user = userMapper.getUserInfo(username);
        if(user == null) {
            throw new UnknownAccountException("用户名不存在");
        }
        password = MD5Util.md5_private_salt(password,user.getSalt());

        // 两个密码的密文进行比对
        if (!user.getPassword().equals(password)) {
            throw new CredentialsException("密码错误");
        }
        if (user.getStatus() == 1) {
            throw new DisabledAccountException("账号被禁用");
        }
        if (user.getStatus() == 2) {
            throw new LockedAccountException("账号被锁定");
        }
        log.info("{}认证成功",username);
        // 创建简单认证信息对象
        SimpleAuthenticationInfo info =
                new SimpleAuthenticationInfo(user, token.getCredentials(), getName());
        return info;
    }


    /**
     * 授权
     * 将认证通过的用户的角色和权限信息设置到对应用户主体上
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        // 简单授权信息对象,对象中包含用户的角色和权限信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //角色
        Set<String> roleNameSet = userMapper.selectUserRoleNameSet(user.getUserId());
        //赋予角色
        info.addRoles(roleNameSet);

        //权限
        Set<String> permissionNameSet = userMapper.selectUserPermissionNameSet(user.getUserId());

        //用户赋予权限
        Set<String> permissions = new HashSet<>();
        for(String name : permissionNameSet) {
            for(String permission : name.split(",")){
                permissions.add(permission);
            }
        }
        info.addStringPermissions(permissions);
        log.info("{}授权完成",user.getUsername());
        return info;
    }
}
登陆功能开发(简单模拟就行)

login.jsp

<%--
  Created by IntelliJ IDEA.
  User: 29086
  Date: 2022/8/30
  Time: 11:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆</title>
    <script type="text/javascript" src="/shiro/js/jquery.min.js"></script>
    <script type="text/javascript" src="/shiro/js/login.js"></script>
    <script type="text/javascript" src="/shiro/js/config.js"></script>
    <style type="text/css">
        .divForm{
            position: absolute;/*绝对定位*/
            width: 300px;
            height: 200px;
            border: 1px solid red;
            text-align: center;/*(让div中的内容居中)*/
            top: 50%;
            left: 50%;
            margin-top: -200px;
            margin-left: -150px;
        }
    </style>
</head>
<body>
<div class="divForm">
    <form id="data-form">
        <p>
            欢迎登陆网站主页
        </p>
        <p>
            用户名:<input type="text" name="username" placeholder="输入用户名"/>
        </p>
        <p>&nbsp;&nbsp;码:<input type="password" name="password"placeholder="输入密码"/>
        </p>
        <p><a href="register.jsp">注册</a></p>
        <input id="submit" type="button" value="提交"/>
    </form>
</div>
</body>
</html>

login.js

$(function () {
    $('#submit').bind('click',function () {
        $('#data-form').submit();
    });
    $("#submit").click(function () {
        $.ajax({
            "url" : httpUrl+"/user/login",
            data: $("#data-form").serialize(),
            "type" : "post",
            "dataType" : "json",
            "success" : function(obj) {
                if (obj.code == 0) {
                    window.location.href = 'index.jsp';
                } else {
                    alert(obj.msg);
                }
            },
            "error":function(){
                alert("系统错误");
            }
        });
    });
});

登陆之后的主页 index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 29086
  Date: 2022/8/30
  Time: 11:37
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>主页</title>
</head>
<body>
<a href="/shiro/user/logout">退出登陆</a>
<ul>
    <li>主页</li>
    <ul>
        <li>控制台</li>
        <li>统计分析</li>
        <ul>
            <li>访客统计</li>
            <li>订单统计</li>
        </ul>
        <li>系统管理</li>
        <ul>
            <li>用户管理</li>
            <ul>
                <li>查询用户</li>
                <li>新增用户</li>
                <li>修改用户</li>
                <li>删除用户</li>
            </ul>
            <li>角色管理</li>
            <ul>
                <li>查询角色</li>
                <li>新增角色</li>
                <li>修改角色</li>
                <li>删除角色</li>
            </ul>
            <li>资源管理</li>
            <ul>
                <li><a href="/shiro/resource/saveResource">新增资源</a></li>
                <li>修改资源</li>
                <li>删除资源</li>
                <li>分配资源</li>
            </ul>
            <li>部门管理</li>
            <ul>
                <li>新增部门</li>
                <li>修改部门</li>
                <li>删除部门</li>
                <li>查询部门</li>
            </ul>
        </ul>
    </ul>
</ul>
</body>
</html>

注册页面 register.jsp

<%--
  Created by IntelliJ IDEA.
  User: 29086
  Date: 2022/8/30
  Time: 11:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册</title>
    <script type="text/javascript" src="/shiro/js/jquery.min.js"></script>
    <script type="text/javascript" src="/shiro/js/register.js"></script>
    <script type="text/javascript" src="/shiro/js/config.js"></script>
    <style type="text/css">
        .divForm{
            position: absolute;/*绝对定位*/
            width: 300px;
            height: 200px;

            border: 1px solid red;
            text-align: center;/*(让div中的内容居中)*/
            top: 50%;
            left: 50%;
            margin-top: -200px;
            margin-left: -150px;
        }
    </style>
</head>
<body>
<div class="divForm">
    <form id="data-form">
        <p>
            用户注册
        </p>
        <p>
            用户名:<input type="text" name="username" placeholder="输入用户名"/>
        </p>
        <p>&nbsp;&nbsp;码:<input type="password" name="password"placeholder="输入密码"/>
        </p>
        <p><a href="login.jsp">不注册,去登陆</a></p>
        <input id="submit" type="button" value="提交"/>
    </form>
</div>
</body>
</html>

register.js

$(function () {
    $('.submit').bind('click',function () {
        $('#data-form').submit();
    });
    $("#submit").click(function () {
        $.ajax({
            "url" : httpUrl+"/user/register",
            data: $("#data-form").serialize(),
            "type" : "post",
            "dataType" : "json",
            "success" : function(obj) {
                if (obj.code == 0) {
                    alert("注册成功");
                    window.location.href = 'login.jsp';
                } else {
                    alert(obj.msg);
                }
            },
            "error":function(){
                alert("系统错误");
            }
        });
    });
});

UserController

import com.shiro.test.model.Result;
import com.shiro.test.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.websocket.server.PathParam;

@Slf4j
@Controller
@RequestMapping("/user")
public class LoginController {

    @Autowired
    private LoginService loginService;

    /**
     * 登录
     * @param username
     * @param password
     * @return
     */
    @PostMapping("/login")
    @ResponseBody
    public Result login(@PathParam("username") String username,@PathParam("password") String password) {
        Result result;
        try {
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            result = Result.ok();
        }catch (UnknownAccountException u){
            result = Result.error("用户名不存在");
        } catch (CredentialsException c){
            result = Result.error("账号或密码错误");
        }catch (LockedAccountException l){
            result = Result.error("账号被禁用");
        } catch (DisabledAccountException d){
            result = Result.error("账号被锁定");
        }
        return result;
    }

    /**
     * 系统退出
     * @return
     */
    @GetMapping("/logout")
    public String logout(){
        // 销毁会话
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }


    /**
     *  用户注册
     * @param username
     * @param password
     * @return
     */
    @PostMapping("/register")
    @ResponseBody
    public Result register(@PathParam("username") String username,@PathParam("password") String password) {
        Result result;
        try {
            result = loginService.register(username,password);
        }catch (Exception e){
            log.info("注册异常信息:{}",e.getMessage());
            result = Result.error("创建账号失败");
        }
        return result;
    }
}

LoginServiceimpl 注册

import com.shiro.test.mapper.UserMapper;
import com.shiro.test.model.Result;
import com.shiro.test.model.User;
import com.shiro.test.model.UserEnum;
import com.shiro.test.service.LoginService;
import com.shiro.test.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;

@Service
public class LoginServiceimpl implements LoginService {


    @Autowired
    private UserMapper userMapper;

    @Override
    public Result register(String username, String password) {
        if(username == null || username == ""){
            return Result.error("用户名不能为空");
        }
        if(password == null || password == ""){
            return Result.error("密码不能为空");
        }
        User user = new User();
        user.setUsername(username);
        String salt = UUID.randomUUID().toString();
        String mdePassword = MD5Util.md5_private_salt(password, salt);
        user.setPassword(mdePassword);
        user.setSalt(salt);
        user.setStatus(UserEnum.zero.getNumber());
        user.setDeleted(UserEnum.zero.getNumber());
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userMapper.insertUser(user);
        return Result.ok();
    }
}

MD5Util

import org.apache.shiro.crypto.hash.Md5Hash;

public class MD5Util {
    // 散列次数
    private static int hashIterations = 3;

    // 内置salt
    private static String public_salt = "958AEF84DB49419689159022A74D547E";

    /**
     * md5加密工具类
     * @param source 要用共盐加密的字符串
     * @return
     */
    private static String md5_public_salt(String source) {
        return new Md5Hash(source, public_salt, hashIterations).toString();
    }

    /**
     *
     * @param source 原始密码
     * @param salt 私盐
     * @return
     */
    public static String md5_private_salt(String source,String salt) {
        //2.再对加密的后密文用私盐加密一次
        return new Md5Hash(md5_public_salt(source), salt, hashIterations).toString();
    }
}

相关功能差不多了,那就看看演示效果,mybatis相关就不过多描述了,写烂了。
在这里插入图片描述
账号密码是admin 123456
在这里插入图片描述

已经登陆进来了,那我debug看看是不是对比密文,可以看到数据库和传入的密文是相等的。
在这里插入图片描述
注册功能就不多讲了,看代码因该能看懂。

shiro 权限控制 (用的比较多的)

主要分三种

  • Java 代码
  • jsp标签
  • Java 注解

jsp标签形式 先导入再jsp中,可以参考index.jsp

<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

判断当前用户是否具有admin角色,如果没有就不显示,有就显示

<shiro:hasRole name="admin"> </shiro:hasRole>

判断当前用户是否有sys:user:add权限,如果没有就不显示,有就显示

<shiro:hasPermission name="sys:user:add"></shiro:hasPermission>

Java 代码方式

		//获取主体对象
        Subject subject = SecurityUtils.getSubject();
        /**
         * 代码方式
         * 基于角色判断
         */
        //判断主体对象是否有权限
        if(subject.hasRole("admin")){
            log.info("有权限");
        }else{
            log.info("无权限");
        }
        /**
         * 代码方式
         * 基于权限判断
         */
        /*if(subject.isPermitted("sys:resource:add")){
            log.info("有权限");
        }else{
            log.info("无权限");
        }*/

Java 注解方式

	//注解方式判断角色是否具有该方法的权限
    //@RequiresRoles("admin")
    //同时具有admin和maintenance角色才可以访问
    //@RequiresRoles(value = {"admin","maintenance"})
    //具有sys:resource:add权限才可以访问
    //@RequiresPermissions("sys:resource:add")
    @Override
    public String addResource() {
        return "redirect:/index.jsp";
    }

相关代码我放百度网盘,需要自取
链接:https://pan.baidu.com/s/1n-e7BSdUmHgLhVvgY-fAYA
提取码:yyds

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值