SpringBoot整合Shiro

1.1 什么是Shiro

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

官网:http://shiro.apache.org/
在这里插入图片描述

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

1.2 Shiro的三个核心组件

三个核心组件:Subject, SecurityManager 和 Realms.

  • Subject,主体,即“当前操作用户”。Subject代表了当前用户的安全操作。(需要被认证的对象)
  • SecurityManager:它是Shiro框架的核心,管理所有用户的安全操作,并提供安全管理的各种服务。
  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
    在这里插入图片描述

在这里插入图片描述

使用Shiro时,需要配置的相关权限过滤器如下(共10个):

在这里插入图片描述
相应解释:

  1. anon: 匿名过滤器,未登陆也可以访问
  2. authc: 认证过滤器, 登陆后访问
  3. perms : 需要xx权限,才能访问
  4. user: 需要xx用户,才能访问
  5. port:指定端口才能访问
  6. ssl:必须使用https协议才能访问
  7. logout :登出功能
  8. rest :根据指定HTTP请求访问才能访问
  9. get方式提交 或者 post方式提交才能访问

2.1 使用shiro做权限控制数据库应该如何设计呢

首先shiro采用的是RBAC认证方式

下面进行传统的权限认证方式和RBAC认证方式的比较

2.1.1 传统的权限认证方式

在这里插入图片描述
特点:为每个人单独的分配权限模块,能够实现权限控制,但是当公司人员庞大之后,非常难管理。

上述权限控制如何设计表?
关系:员工和菜单权限的关系:多对多

员工id菜单名称
1取派管理
2快递员管理
2运单管理

好处:可以方便的 实现权限控制

缺陷:比如当修改权限的时候,公司统一的给组长级别的人 加一个“计算工资”权限,这时候,得修改权限表中所有组长的权限,每个组长在数据库中都得增加一条“计算工资”记录的权限

后来,这个“计算工资”的功能,在给组长之后,发现,这个权限不合适,得收回这个权限,这个时候,需要删除多条记录

2.1.2 RBAC认证方式

Role Based Access Controller :基于角色的访问控制
前无古人后无来者
在这里插入图片描述

其实到这,RBAC认证方式的优点已经显而易见了。

RBAC认证方式下的数据库设计如图所示:

在这里插入图片描述

好了,到这你已经大概了解Shiro是干嘛用的了。
下面是SpringBoot整合Shiro的简单使用

3.1 SpringBoot整合Shiro

前提:能独力创建SpringBoot项目(SpringBoot快速入门
步骤1:SpringBoot项目已经创建好了,导入maven坐标

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.czxy</groupId>
    <artifactId>shiro-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>shiro-demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- 通用Mapper启动器 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
            <scope>runtime</scope>
        </dependency>
        <!--shiro start-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--shiro end-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

步骤2:导入sql语句

/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 50559
 Source Host           : localhost:3306
 Source Schema         : shiro_demo

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

 Date: 09/12/2018 21:11:53
 author https://blog.csdn.net/chen_2890
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `pid` int(11) NOT NULL AUTO_INCREMENT,
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `keyword` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1005 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1001, '添加功能', 'add', '添加');
INSERT INTO `permission` VALUES (1002, '查询功能', 'select', '查询');
INSERT INTO `permission` VALUES (1003, '更新功能', 'update', '更新');
INSERT INTO `permission` VALUES (1004, '删除功能', 'delete', '删除');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `rid` int(11) NOT NULL AUTO_INCREMENT,
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `keyword` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1004 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1001, '普通用户', 'Commom', '普通用户');
INSERT INTO `role` VALUES (1002, '一般会员', 'Member', '会员用户');
INSERT INTO `role` VALUES (1003, '超级会员', 'Vip', 'VIP用户');

-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`  (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1001, 1001);
INSERT INTO `role_permission` VALUES (1001, 1002);
INSERT INTO `role_permission` VALUES (1002, 1001);
INSERT INTO `role_permission` VALUES (1002, 1002);
INSERT INTO `role_permission` VALUES (1002, 1003);
INSERT INTO `role_permission` VALUES (1003, 1002);
INSERT INTO `role_permission` VALUES (1003, 1001);
INSERT INTO `role_permission` VALUES (1003, 1003);
INSERT INTO `role_permission` VALUES (1003, 1004);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'jack', 'ae7f487d56152e165afdfd87c2b819a5', 18);
INSERT INTO `user` VALUES (2, 'tom', 'cc804223edc8063d7b3d9dc94b81fba3', 19);
INSERT INTO `user` VALUES (3, 'rose', 'c89f94fdfb8ae723413296a03c0f8d3b', 20);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `role_id` int(11) NOT NULL,
  `uid` int(11) NOT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1001, 1);
INSERT INTO `user_role` VALUES (1001, 2);
INSERT INTO `user_role` VALUES (1002, 2);
INSERT INTO `user_role` VALUES (1001, 3);
INSERT INTO `user_role` VALUES (1002, 3);
INSERT INTO `user_role` VALUES (1003, 3);

SET FOREIGN_KEY_CHECKS = 1;

步骤3:导入Shiro配置类

package com.czxy.config;


import com.czxy.shiro.ShiroCredentialsMatcher;
import com.czxy.shiro.ShiroRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
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 java.util.LinkedHashMap;
import java.util.Map;


/**
 * 功能描述: 在ShiroConfig中做什么事情呢?
 * 1 配置shiro安全管理器,向安全管理器中注入Realm域
 * 2 配置Realm域:注入密码比较器
 * 3 配置密码比较器
 * 4 配置拦截路径和放行路径
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/9 20:32
 */
@Configuration
public class ShiroConfig {

    /**
     * 配置安全管理器,并且注入Realm域
     * @param realm
     */
    @Bean
    public SecurityManager securityManager(Realm realm){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     *  Credentials:凭证/证书 ---
     *
     * 配置Realm域,注入密码比较器
     * @param credentialsMatcher
     */
    @Bean
    public ShiroRealm realm(CredentialsMatcher credentialsMatcher){
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(credentialsMatcher);
        return shiroRealm;
    }
    /**
     * 密码比较器
     */
    @Bean
    public CredentialsMatcher credentialsMatcher(){
        return new ShiroCredentialsMatcher();
    }

    /**
     * 配置拦截路径和放行路径
     * @param securityManager
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
        // shiro过滤器工厂类
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器----Map集合
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/login*", "anon");
        filterChainDefinitionMap.put("/index.html*", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        //根据用户角色赋予相应的权限
        filterChainDefinitionMap.put("/add-success.html", "roles[Commom]");
        filterChainDefinitionMap.put("/selete-success.html", "roles[Commom]");
        filterChainDefinitionMap.put("/update-success.html", "roles[Member]");
        filterChainDefinitionMap.put("/delete-success.html", "roles[Vip]");
        //根据用户拥有的具体权限赋予相应的权限
        filterChainDefinitionMap.put("/add-success.html", "perms[add]");
        filterChainDefinitionMap.put("/selete-success.html", "perms[select]");
        filterChainDefinitionMap.put("/update-success.html", "perms[update]");
        filterChainDefinitionMap.put("/delete-success.html", "perms[delete]");
        //   /** 匹配所有的路径
        //  通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
        //  如果下面的定义与上面冲突,那按照了谁先定义谁说了算
        //  /** 一定要配置在最后
        filterChainDefinitionMap.put("/**", "authc");
        // 将拦截器链设置到shiro中
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/index.html");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/button.html");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/power.html");

        return shiroFilterFactoryBean;
    }

    /**
     * 开启shiro aop注解支持
     * 使用代理方式;所以需要开启代码支持
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 开启cglib代理
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
}

步骤4:创建Realm域

package com.czxy.shiro;

import com.czxy.domain.Permission;
import com.czxy.domain.Role;
import com.czxy.domain.User;
import com.czxy.service.PermissionService;
import com.czxy.service.RoleService;
import com.czxy.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.List;


/**
 * 功能描述:ShiroRealm域
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/5 19:42
 */
public class ShiroRealm extends AuthorizingRealm {

    /**
     * 描述:userService对象
     * @date 2018/12/9 20:54
     */
    @Autowired
    private UserService userService;

    /**
     * 描述:roleService对象
     * @date 2018/12/9 20:54
     */
    @Autowired
    private RoleService roleService;

    /**
     * 描述:permissionService对象
     * @date 2018/12/9 20:54
     */
    @Autowired
    private PermissionService permissionService;


    /**
     * 功能描述:shiro认证
     * @param token
     * @return org.apache.shiro.authc.AuthenticationInfo
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/5 19:42
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户输入的用户名和密码
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //获取用户名
        String username = upToken.getUsername();
        //根据用户名去数据库查询
        User user = userService.findUserByUsername(username);
        //用户名不存在
        if(user == null){
            return null;
        }
        //用户名存在,进去密码比较器比较密码
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());

        return authenticationInfo;
    }

    /**
     * 功能描述:shiro授权
     * @param principal
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/9 20:55
     **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //创建授权对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //获取已经认证通过的用户信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        //根据用户信息查找对应的角色
        List<Role> roleList = roleService.findRoleByUser(user);
        for (Role role : roleList) {
            authorizationInfo.addRole(role.getKeyword());
        }
        //根据用户信息查找对应的权限
        List<Permission> permissionList = permissionService.findPermissionByUser(user);
        for (Permission permission : permissionList) {
            authorizationInfo.addStringPermission(permission.getKeyword());
        }
        return authorizationInfo;
    }


}

步骤5:创建shiro认证器

package com.czxy.shiro;

import com.czxy.util.Encrypt;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

/**
 * 功能描述:shiro认证器:ShiroCredentialsMatcher
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/9 20:47
 */
public class ShiroCredentialsMatcher extends SimpleCredentialsMatcher {

    
    /**
     * 功能描述:shiro的密码比较器
     * @param token : 用户页面输入的信息
     * @param info : 数据库中的信息
     * @return boolean
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/9 20:49
     **/
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //获取用户输入的用户名和密码
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //得到密码凭证
        char[] pwd = upToken.getPassword();
        //转换为string类型
        String myPwd = new String(pwd);
        //将用户输入的密码转为密文
        String newPwd = Encrypt.md5(myPwd, upToken.getUsername());
        //获取数据库中的密文密码
        Object dbPwd = info.getCredentials();
        //密码验证
        return equals(newPwd, dbPwd);
    }
}

步骤6:导入加密工具类

package com.czxy.util;

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

/**
 * 功能描述:加密工具
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/5 23:29
 */
public class Encrypt {

    /**
     * 功能描述:高强度加密算法,不可逆
     * @param password:密码
     * @param salt:盐
     * @return java.lang.String
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/5 23:29
     **/
    public static String md5(String password, String salt){
        return new Md5Hash(password,salt,2).toString();
    }

    /**
     * 功能描述:用于测试的main方法
     * @param args
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/5 23:31
     **/
    public static void main(String[] args) {

        //ae7f487d56152e165afdfd87c2b819a5
        System.out.println(new Md5Hash("123456","jack",2).toString());
        //cc804223edc8063d7b3d9dc94b81fba3
        System.out.println(new Md5Hash("123456","tom",2).toString());
        //c89f94fdfb8ae723413296a03c0f8d3b
        System.out.println(new Md5Hash("123456","rose",2).toString());
        System.out.println();
        System.out.println(md5("123456","jack"));
        System.out.println(md5("123456","tom"));
        System.out.println(md5("123456","rose"));

    }
}

步骤7:编写LoginController控制器

package com.czxy.controller;

import com.czxy.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;



/**
 * 功能描述:LoginController
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/5 19:41:01
 */
@RestController
public class LoginController {

    /**
     * 描述:session对象
     * @date 2018/12/9 20:35
     */
    @Autowired
    private HttpSession session;

    /**
     * 功能描述:login,即shiro的认证
     * @param user
     * @return org.springframework.http.ResponseEntity<java.lang.Void>
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/9 20:35
     **/
    @GetMapping("/login")
    public ResponseEntity<Void> login(User user){
        //1 接收页面参数,转成对象----系统自动完成了
        //2 获取Subject对象
        Subject subject = SecurityUtils.getSubject();
        //3 Subject启动Shiro
        // 准备数据
        UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try{
            subject.login(upToken);
            // 能够执行到这步,肯定登录已经成功
            // 获取用户信息,保存到session中
            User loginUser = (User) subject.getPrincipal();
            // 放入session中
            session.setAttribute("loginUser",loginUser);
            //返回状态
            return new ResponseEntity<>(HttpStatus.OK);

        } catch ( UnknownAccountException uae ) {
            //用户名未知...
            System.out.println("用户不存在");
        } catch ( IncorrectCredentialsException ice ) {
            //凭据不正确,例如密码不正确 ...
            System.out.println("密码不正确");
        } catch ( LockedAccountException lae ) {
            //用户被锁定,例如管理员把某个用户禁用...
            System.out.println("用户被禁用");
        } catch ( ExcessiveAttemptsException eae ) {
            //尝试认证次数多余系统指定次数 ...
            System.out.println("请求次数过多,用户被锁定");
        } catch ( AuthenticationException ae ) {
            //其他未指定异常
            System.out.println("未知错误,无法完成登录");
        }
        //返回状态
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 功能描述: shiro的logout退出,只是将放置PrincipalCollection这个集合置空,
     * 删除了session,但是没有清空缓存,需要手动清除缓存
     * @return org.springframework.http.ResponseEntity<java.lang.Void>
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/9 20:35
     **/
    @PutMapping("/logout")
    public ResponseEntity<Void> logout(){
        //清空session
        session.removeAttribute("loginUser");
        //退出shiro
        SecurityUtils.getSubject().logout();
        //返回状态
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

最后一步:核心代码已全部给出,最后一步就是创建相应的类实现方法了。

考虑到有人实在是懒,下面已经备好了一份demo,仅供参考。

shiro-demo.rar
提取码:seq6

好了,到这里SpringBoot整合Shiro完毕了
要是还有不太明白的地方请留言,评论必回
要是对我的文章感兴趣的话,关注一下吧,谢谢!

上一篇:SpringBoot整合Elasticsearch
下一篇:待续。。。
给我们一个微信勾搭的机会

在这里插入图片描述

根据引用,创建一个Spring Boot项目并引入Shiro依赖。使用以下代码创建一个配置类来配置Shiro环境。在该配置类中,您可以定义Shiro的各种配置项,例如Realm、Session管理器等。 ```java @Configuration public class ShiroConfig { // 配置Shiro的安全管理器 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); return securityManager; } // 自定义的Realm @Bean public MyRealm myRealm() { return new MyRealm(); } // 配置Shiro的过滤器链 @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); chainDefinition.addPathDefinition("/user/logout", "logout"); chainDefinition.addPathDefinition("/**", "authc"); return chainDefinition; } // 配置Shiro的注解支持 @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setProxyTargetClass(true); return autoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } } ``` 根据引用,开发一个UserController,并在其中定义一个logout()方法,用于退出登录。在该方法中,首先获取当前用户的Subject对象,然后调用subject.logout()方法退出登录。最后,使用重定向将用户重定向到登录页面。 ```java @Controller @RequestMapping("user") public class UserController { @RequestMapping("logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout();//退出用户 return "redirect:/login.jsp"; } } ``` 根据引用,根据不同的Spring Boot版本,Shiro的请求处理方式可能会有所不同。在Spring Boot 2.7.10中,请求会正常进入Shiro的过滤器中(org.apache.shiro.web.servlet.OncePerRequestFilter)进行处理。 希望这些信息能对您有所帮助!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Shiro简单配置Springboot版(3)](https://blog.csdn.net/qq_39390455/article/details/109214894)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [SpringBoot3整合Shiro未生效问题](https://blog.csdn.net/banmao999/article/details/131420519)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值