springboot集成shiro+redis做认证和授权

权限效果

角色有3个,管理员,老师,学生
1.管理员可访问(老师+学生的,主页,查询,删除)
2.老师可访问(老师的:主页,查询)(学生的:主页,删除)
3.学生可访问(学生的:主页,查询)

项目目录结构

在这里插入图片描述

引入jar包

<?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>org.example</groupId>
    <artifactId>springboot_shiro_redis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <shiro.version>1.8.0</shiro.version>
    </properties>


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

    <dependencies>
        <!-- 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- spring 容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis-spring-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
    </dependencies>

</project>

配置文件

yml配置文件

server:
  port: 8088
spring:
  thymeleaf:
    cache: false

shiro-redis.properties

#--------------------shiro-redis配置------------------------
#redis地址
shiro-redis.redis-manager.host=127.0.0.1:6379
#redis密码
#shiro-redis.redis-manager.password=12345
#用户信息存入redis第几个库
shiro-redis.redis-manager.database=5
#实体类id,默认是id,找不到id则会报错
shiro-redis.cache-manager.principal-id-field-name=userId
#自定义redis关键字前缀的会话管理
shiro-redis.session-dao.key-prefix=token:user-session:
#自定义redis关键字前缀缓存管理
shiro-redis.cache-manager.key-prefix=token:authorization:
#--------------------shiro-redis配置------------------------

spring-shiro.properties

#---------shiro配置--------------------------------------
#登录页面
shiro.loginUrl=/toLogin.htm
#无权限访问
shiro.unauthorizedUrl=/error
shiro.userNativeSessionManager=true
#禁用URL会话重写
shiro.sessionManager.sessionIdUrlRewritingEnabled=false
#自定义cookie名字,默认JSESSIONID
shiro.sessionManager.cookie.name=Authorization
#---------shiro配置--------------------------------------

logback.xml

<?xml version="1.0" encoding="utf-8"?>
<configuration scan="false">
	<!--value="%d{HH:mm:ss.SSS} [%thread] %-5level %caller{3} %c: %L - %msg%n" -->
	<property name="format" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M: %n %replace(%caller{1}){'\t|Caller.{1}0| at|\r\n', ''} : %msg %n" />

	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<!-- encoder 默认配置为PatternLayoutEncoder -->
		<encoder>
			<pattern>${format}</pattern>
		</encoder>
	</appender>

	<!-- 设置日志(访问日志,系统日志)输出位置以及格式 -->
	<appender name="INFOLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<FileNamePattern>${LOG_HOME}/DEBUGLOG/${HOSTNAME}_%d{yyyy-MM-dd}.%i.log</FileNamePattern>
			<cleanHistoryOnStart>true</cleanHistoryOnStart>
			<!-- 根据日志文件按天回滚,保存时间为7天,7天之前的都将被清理掉 -->
			<maxHistory>7</maxHistory>
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- 每个日志文件保存最大值 -->
				<maxFileSize>1MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder>
			<pattern>[%date] [%thread] [%level] %msg%n</pattern>
			<charset>UTF-8</charset>
		</encoder>
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<!-- 只打印INFO日志 -->
			<level>INFO</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>

	<logger name="cn.itgsvip" level="info" >
		<appender-ref ref="INFOLOG" />
	</logger>
	<root level="info">
		<appender-ref ref="STDOUT" />
	</root>

</configuration>

实体类

package com.demo.vo;

import lombok.Data;

import java.io.Serializable;

/**
 * @author 作者:wl
 */
@Data
public class UserInfoVO implements Serializable {
	private static final long serialVersionUID = 1L;
	private String userId;//用户id
	private String userName;//用户名
	private String password;//用户密码
}

业务层

接口

package com.demo.service;

import com.demo.vo.UserInfoVO;

/**
* @author 作者:wl
*/
public interface UserInfoService {
	//login登录
	UserInfoVO login(String username, String password);
}

实现类

package com.demo.service.impl;

import org.springframework.stereotype.Service;

import com.demo.service.UserInfoService;
import com.demo.vo.UserInfoVO;

/**
* @author 作者:wl
*/
@Service
public class UserInfoServiceImpl implements UserInfoService {
	//注入DAO实现数据库查询
	@Override
	public UserInfoVO login(String username, String password) {
		UserInfoVO userInfo = new UserInfoVO();
		/**模拟数据库账号,老师角色 **/

		if("teacher".equals(username)) {
			userInfo.setUserId("10001");
			userInfo.setUserName(username);
			userInfo.setPassword(password);
		}
		/**模拟数据库账号,学生角色 **/
		if("student".equals(username)) {
			userInfo.setUserId("10002");
			userInfo.setUserName(username);
			userInfo.setPassword(password);
		}
		/**模拟数据库账号,管理员 **/
		if("admin".equals(username)) {
			userInfo.setUserId("10003");
			userInfo.setUserName(username);
			userInfo.setPassword(password);
		}
		return userInfo;
	}

}

shiro配置类 BootShiroRedisAutoConfiguration

package com.demo.config;

import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
@Configuration用于定义配置类,可替换xml配置文件,
被注解的类内部包含有一个或多个被@Bean注解的方法,
这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,
并用于构建bean定义,初始化Spring容器。
 */
@Configuration
public class BootShiroRedisAutoConfiguration extends ShiroWebAutoConfiguration {

	@Autowired
	RedisSessionDAO redisSessionDAO;

	/**
	 * <p>@方法名描述	:	Shiro过滤器 </p> 
	 * <per> 
	 * 		<code>
	 * 			anon  ===>  开放路径,允许匿名访问,不需要权限和不需要登录就可以访问 
	 * 			authc ===>  需要登录后才可以访问路径
	 * 		</code>
	 * </per> 
	 * @return
	 */
	@Bean("shiroFilterChainDefinition")
	public ShiroFilterChainDefinition shiroFilterChainDefinition() {
		DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
		chainDefinition.addPathDefinition("/api/login.htm", "anon");
		chainDefinition.addPathDefinition("/**", "authc");
		return chainDefinition;
	}

	@Bean("authorizer")
	public SampleRealm sampleRealm() {//把自定义Realm交给spring容器
		return new SampleRealm();
	}

	@Override
	protected SessionManager sessionManager() {
		if (useNativeSessionManager) {
			DefaultWebSessionManager nativeSessionManager = (DefaultWebSessionManager) nativeSessionManager();
			//Redis Expire 命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。
			//设置redissession失效时间,秒单位
			redisSessionDAO.setExpire(60);
			nativeSessionManager.setSessionDAO(redisSessionDAO);
			return nativeSessionManager;
		}
		return new ServletContainerSessionManager();
	}
}

自定义Realm SampleRealm

package com.demo.config;

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

import com.demo.service.UserInfoService;
import com.demo.vo.UserInfoVO;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.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;


public class SampleRealm extends AuthorizingRealm {
	
	@Autowired
	private com.demo.service.UserInfoService UserInfoService;
	
	
	//授权管理
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//principals 转成UserInfoVO对象
		UserInfoVO user = (UserInfoVO) principals.getPrimaryPrincipal();
		//利用SimpleAuthorizationInfo对象做授权
		SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();
		/** 模拟老师角色 **/
		if("teacher".equals(user.getUserName())) {
			System.out.println("teacher");
			//授予对应角色的权限
			sai.addRole("teacher");
			//可以加集合权限
			Set<String> permissions = new HashSet<String>();
			permissions.add("teacher:find");
			permissions.add("student:del");//删学生
			sai.addStringPermissions(permissions);
		}
		/** 模拟学生角色 **/
		if("student".equals(user.getUserName())) {
			System.out.println("student");
			//授予对应角色的权限
			sai.addRole("student");
			//可以加集合权限
			Set<String> permissions = new HashSet<String>();
			permissions.add("student:find");
			sai.addStringPermissions(permissions);
		}
		/** 模拟管理员角色 **/
		if("admin".equals(user.getUserName())) {
			System.out.println("admin");
			//授予对应角色的权限
			sai.addRole("teacher");
			sai.addRole("student");
			//可以加集合权限
			Set<String> permissions = new HashSet<String>();
			permissions.add("teacher:find");//查老师
			permissions.add("teacher:del");//删老师
			permissions.add("student:find");//查学生
			permissions.add("student:del");//删学生
			sai.addStringPermissions(permissions);
		}
		return sai;
	}
	//认证管理
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
		//取出controller的login方法里面根据username和password生成的token
		UsernamePasswordToken token = (UsernamePasswordToken) authToken;
		String username = token.getUsername();
		String password = String.valueOf(token.getPassword());
		//以此来做认证(调用业务来做认证)
		//从数据库中取出账号密码
		UserInfoVO userInfoVO = UserInfoService.login(username, password);
		return new SimpleAuthenticationInfo(userInfoVO,password,getName());
	}
}

异常拦截器 ShiroExceptionAdvice

package com.demo.config;

import org.apache.shiro.authz.AuthorizationException;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @Auther:wl
 */
@ControllerAdvice
public class ShiroExceptionAdvice {
    /**
     * 无权限异常 </p>
     * @param e AuthorizationException异常
     * @return 403无权限页面
     */
    @ExceptionHandler(AuthorizationException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Object handleException(AuthorizationException e, Model model) {
        System.out.println("AuthorizationException================>" + e.getMessage());
        model.addAttribute("code", HttpStatus.FORBIDDEN.value());
        return "403";
    }
}

控制层

LoginController

package com.demo.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 作者:wl
 * 需求:
 * 不同账号登录,根据权限分配,可以访问不同的页面
 * 如果越权访问,就显示无权访问.....
 */
@Controller
public class LoginController {
    //跳转登录页面
    @RequestMapping("toLogin.htm")
    public String toLogin() {
        return "login";
    }

    //shiro认证登录接口
    @RequestMapping("api/login.htm")
    public String apiLogin(String username, String password) {
        /**
         * 把账号密码交给shiro管理
         * 以后可以根据账号密码来做认证和授权
         * UsernamePasswordToken
         *   可以基于账号密码生成一个token
         * shiro可以利用token对subject对象进行认证和授权
         *
         */
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        /** 登录,交给 shiro 处理,实际登录处理逻辑还是得自己写,处理了个寂寞  **/
        SecurityUtils.getSubject().login(token);
        return "index";//return "redirect:/index";  redirect可以省略不写
    }

    /**
     * :退出接口 </p>
     * @return 转发到登录页面
     */
    @RequestMapping("api/logout")
    public String logout() {
        /** 退出,交给Shiro处理 **/
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }

}

TeacherController

package com.demo.controller;

import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 老师的Controller
 * 	1.管理员和老师可以访问主页
 * 	2.管理员和老师可以访问查询页
 * 	3.管理员可以访问删除
 */
@RestController
@RequestMapping("teacher")
public class TeacherController {

	/**
	 * teacher角色可以访问的路径
	 * @return
	 */
	//属于 teacher 角色
	@RequiresRoles("teacher")
	@RequestMapping
	public String adminIndex() {
		return "老师的主页权限";
	}

	/**
	 * teacher:find权限可以访问的路径
	 * @return
	 */
	//符合teacher:find权限要求
	@RequiresPermissions("teacher:find")
	@RequestMapping("find")
	public String adminFind() {
		return "老师的查询权限";
	}

	/**
	 * 拥有 teacher和admin角色 和 teacher:del权限可以访问的路径
	 * @return
	 */
	//属于 teacher 或者 admin 之一;修改logical为OR 即可
	@RequiresRoles(logical = Logical.OR, value = { "teacher", "admin" })
	//符合teacher:del 权限要求
	@RequiresPermissions("teacher:del")
	@RequestMapping("del")
	public String adminDel() {
		return "老师的删除权限";
	}
}

StudentController

package com.demo.controller;

import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 学生的Controller
 * 	1.管理员和学生可以访问主页
 * 	2.管理员和学生可以访问查询页
 * 	3.管理员和老师可以访问删除
 */
@RestController
@RequestMapping("student")
public class StudentController {

    /**
     * teacher角色可以访问的路径
	 * @return
     */
	@RequiresRoles("student")
	@RequestMapping
	public String adminIndex() {
		return "学生的主页权限";
	}

    /**
     * teacher:find权限可以访问的路径
	 * @return
     */
	@RequiresPermissions("student:find")
	@RequestMapping("find")
	public String adminFind() {
		return "学生的查询权限";
	}

    /**
     * 拥有 teacher和admin角色 和 teacher:del权限可以访问的路径
	 * @return
     */
	@RequiresRoles(logical = Logical.OR, value = { "teacher", "admin" })
	@RequiresPermissions("student:del")
	@RequestMapping("del")
	public String adminDel() {
		return "学生的删除权限";
	}
}

前端 templates

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head fragment="head">
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta name="viewport" content="width=device-width"/>
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
          rel="stylesheet" type="text/css"/>
    <link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
          type="text/css"/>

    <style>
        body {
            margin-top: 60px;
        }

        .box {
            padding: 50px;
            text-align: center;
            vertical-align: middle;
        }

        .custom-header {
            border: 2px solid #3254a0;
        }
    </style>
<body>
<div class="container">


    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <p>springboot快速整合shiro</p>
            <table class="table">
                <thead>
                <tr>
                    <th>用户名</th>
                    <th>密码</th>
                    <th>角色</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>teacher</td>
                    <td>123123</td>
                    <td>老师</td>
                </tr>
                <tr>
                    <td>student</td>
                    <td>123123</td>
                    <td>学生</td>
                </tr>
                <tr>
                    <td>admin</td>
                    <td>123123</td>
                    <td>管理员</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">登录</h3>
                </div>
                <div class="panel-body">
                    <form name="loginform" action="api/login.htm" method="POST" accept-charset="UTF-8" role="form">
                        <fieldset>
                            <div class="form-group">
                                <input class="form-control" placeholder="请输入账号" name="username" type="text"/>
                            </div>
                            <div class="form-group">
                                <input class="form-control" placeholder="请输入密码" name="password" type="password"/>
                            </div>

                            <input class="btn btn-lg btn-success btn-block" type="submit" value="登录"/>
                        </fieldset>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="https://code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
</body>
</html>

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head fragment="head">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width" />
<link
	href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
	rel="stylesheet" type="text/css" />
<link
	href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
	rel="stylesheet" type="text/css" />

<style>
	<style>
	body {
		margin-top: 60px;
	}
	.box {
		padding: 50px;
		text-align: center;
		vertical-align: middle;
	}
	.custom-header {
		border: 2px solid #3254a0;
	}
</style>
<body>
<div class="container">
	<div class="row">
		<div class="col-md-4 col-md-offset-4">
			<h3>老师权限操作</h3>
			<a href="/teacher">teacher:老师主页权限</a><br />
			<a href="/teacher/find">find:老师查询权限</a><br />
			<a href="/teacher/del">del:老师删除权限</a><br />
		</div>
		<div class="col-md-4 col-md-offset-4">
			<h3>学生权限操作</h3>
			<a href="/student">student:学生主页</a><br />
			<a href="/student/find">find:学生查询权限</a><br />
			<a href="/student/del">del:学生没有删除权限,只有老师和管理员有</a><br />
		</div>
		<div class="col-md-4 col-md-offset-4">
			<a href="/toLogin.htm">登录页面</a><br />
		</div>
		<div class="col-md-4 col-md-offset-4">
			<a href="/api/logout">退出登录页面</a><br />
		</div>
	</div>
</div>
<script src="https://code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
</body>
</html>

403.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <meta name="viewport" content="width=device-width"/>
  <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
        rel="stylesheet" type="text/css"/>
  <link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
        type="text/css"/>
</head>
<body>
<div class="container-fluid">
  <div class="row">
    <div class="box col-md-6 col-md-offset-3">
      <div class="custom-header">
      </div>
      <div class="logo">
        <h1 th:text="${code}"></h1>
      </div>
      <p class="lead text-muted">你的权限不足....</p>
      <a href="/" class="btn btn-primary">Go Home</a>
    </div>
  </div>
</div>
</body>
</html>

测试

启动redis 和 客户端

redis客户端

在这里插入图片描述

启动项目浏览器访问

浏览器

在这里插入图片描述

redis客户端

可以看到redis里面存了一个key为user-session,value为浏览器request请求头里面Cookies的Authorization的值
在这里插入图片描述

admin登入成功后点击老师主页

在这里插入图片描述

redis客户端

可以看到redis里面存了一个key为authorization,value为自定义的Realm的信息
在这里插入图片描述

teacher登入成功后点击删除老师

在这里插入图片描述
同样可以看到redis里面存了一个key为authorization,value为自定义的Realm的信息
在这里插入图片描述

session消失后,再点击老师主页,就会重定向到登录页面

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值