文章目录
Shiro概述
主要特征
Shiro提供了如上图所示的特性,其中主要特性(其开发团队称之为应用安全的四大基石)如下:
- Authentication - 身份认证 (与登陆相关,确定用户是谁)
- Authorization - 确认权限 (确定用户能访问什么)
- Session Management - 会话管理
- Cryptography - 数据加密
shiro如何工作
应用代码的交互对象是 “Subject”,该对象代表了当前 “用户”,而所有用户的安全操作都会交给 SecurityManager 来管理,而管理过程中会从 Realm 中获取用户对应的角色和权限,可以把 Realm 堪称是安全数据源
使用最简单的 Shiro 应用:
- 通过 Subject 来进行认证和授权,而 Subject 又委托给了 SecurityManager 进行管理
- 我们需要给 SecurityManager 注入 Realm 以便其获取用户和权限进行判断
- (也即,Shiro 不提供用户和权限的维护,需要由开发者自行通过 Realm 注入)
内部详细流程
三个核心组件:Subject, SecurityManager 和 Realms
- Subject:即“当前操作用户”,它仅仅意味着“当前跟软件交互的东西”
- SecurityManager:它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务
- Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了
- Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能
- SessionManager:管理 Session 生命周期的组件;会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;
- Realm:充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个
- CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
- Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密
官方快速开始代码分析
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.ini.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.lang.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
// 获取当前的用户对象
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
// 通过当前用户获得Session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let's login the current user so we can check against roles and permissions:
// 判断当前用户是否被认证
if (!currentUser.isAuthenticated()) {
// 通过用户名和密码生成token
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
// 设置RememberMe
token.setRememberMe(true);
try {
// 执行登录
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
shiro.ini
# =============================================================================
# Quickstart INI Realm configuration
#
# For those that might not understand the references in this file, the
# definitions are all based on the classic Mel Brooks' film "Spaceballs". ;)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5
总结核心代码
// 获取当前的用户对象
Subject currentUser = SecurityUtils.getSubject();
// 通过当前用户获得Session
Session session = currentUser.getSession();
// 判断当前用户是否被认证
currentUser.isAuthenticated
// 获取当前用户认证
currentUser.getPrincipal()
// 获取当前用户被认证的角色
currentUser.hasRole("schwartz")
// 获取用户的权限
currentUser.isPermitted("lightsaber:wield")
// 执行退出
currentUser.logout();
Springboot整合Shiro
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
创建配置类ShiroConfig
package com.jason.shiro.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @authot jason_yan
* @date 2020/6/17-11:10
*/
@Configuration
public class ShirConfig {
// 3、ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
// 关联 securityManager
filterFactoryBean.setSecurityManager(securityManager);
return filterFactoryBean;
}
// 2、DefaultWebSecurityManager
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
// 1、创建 realm 对象
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
创建Realm
继承于 AuthorizingRealm
package com.jason.shiro.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @authot jason_yan
* @date 2020/6/17-11:17
*/
public class UserRealm extends AuthorizingRealm {
// 授权操作
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权AuthorizationInfo");
return null;
}
// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证AuthenticationInfo");
return null;
}
}
创建控制器
package com.jason.shiro.controller;
import com.sun.org.apache.xpath.internal.operations.Mod;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @authot jason_yan
* @date 2020/6/17-10:55
*/
@Controller
public class IndexController {
@GetMapping({"/index","/"})
public String toIndex(Model model){
model.addAttribute("msg","hello shiro");
return "index";
}
@GetMapping("/add")
public String toAdd(){
return "add";
}
@GetMapping("/update")
public String toUpdate(){
return "update";
}
}
创建静态页面
add和update页面 略(页面内容随便写)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a th:href="@{/add}">add something</a>
<a th:href="@{/update}">update something</a>
</body>
</html>
配置过滤器
// 3、ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
// 关联安全管理器
filterFactoryBean.setSecurityManager(securityManager);
// 配置过滤器
/**
* 配置哪些页面需要受保护. 以及访问这些页面需要的权限.
* 1). anon 可以被匿名访问,即不需要登录即可访问
* 2). authc 必须认证(即登录)后才可能访问的页面.
* 3). user 必须拥有 记住我 才能使用
* 4). perms 拥有对某个资源的权限才能访问
* 5). roles 拥有某个角色才能访问
* 6). logout 当前用户退出登录
*/
Map<String,String> filterMap = new LinkedHashMap();
filterMap.put("/index","anon");
filterMap.put("/add","authc");
filterMap.put("/update","authc");
filterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 如果没有认证(即登录)进行登录请求
filterFactoryBean.setLoginUrl("/login");
return filterFactoryBean;
}
控制器实现跳转至登录页面
@GetMapping("/login")
public String toLogin(){
return "login";
}
配置登录页面 login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录页面</h1>
<hr>
<form th:action="@{/do/login}">
<p>username:<input type="text" name="username" placeholder="用户名"/></p>
<p>password:<input type="password" name="password" placeholder="密码"/></p>
<p><input type="submit" value="登录"></input></p>
</form>
</body>
</html>
当用户没有进行登录的时候 访问 add something update something 的 时候需要进行登录,才可以访问
配置Realm认证方法
在控制器中执行登录操作
@GetMapping("/do/login")
public String login(String username, String password, Model model){
// 获取已登录的用户信息
Subject subject = SecurityUtils.getSubject();
// 封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
// 执行登录
subject.login(token);
// 登陆成功返回首页
return "index";
} catch (UnknownAccountException uae) {
model.addAttribute("msg","用户名错误");
// 同时在登录页面login.html中 获取错误信息<p th:text="${msg}" style="color: red"></p>
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "login";
}
}
当用户登陆信息提交后,在Realm中的doGetAuthenticationInfo()方法中对用户进行认证(查数据库匹配信息)
// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证AuthenticationInfo");
// token是从controller中传递过来的,在此处实现真正的登录操作
// 在数据库中获取用户名和密码
String name = "admin";
String password = "111111";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 判断从前端输入的用户名与数据库中用户名是否匹配
if (!userToken.getUsername().equals(name)){
return null; // 自动抛出UnknownAccountException异常
}
// 密码认证,使用shiro的SimpleAuthenticationInfo实现
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo("", password, "");
return info;
}
Shiro整合Mybatis
导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
创建数据库db_shiro并创建用户角色表
添加用户:admin,zs,ls
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
配置 application.yml 文件
spring:
datasource:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_shiro?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.jason.shiro.entity
filters: stat,wall,log4j
maxPoolPrearedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
创建User
package com.jason.shiro.entity;
import lombok.*;
/**
* @authot jason_yan
* @date 2020/6/17-15:13
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
}
在resource目录下(classpath)创建mapper文件夹 并配置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.jason.shiro.mapper.UserMapper" >
<select id="queryUserByName" parameterType="String" resultType="User">
select * from tb_user where username = #{username}
</select>
</mapper>
创建Mapper接口
package com.jason.shiro.mapper;
import com.jason.shiro.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* @authot jason_yan
* @date 2020/6/17-15:23
*/
@Repository
@Mapper
public interface UserMapper {
public User queryUserByName(String username);
}
创建service
package com.jason.shiro.servce;
import com.jason.shiro.entity.User;
/**
* @authot jason_yan
* @date 2020/6/17-15:34
*/
public interface UserService {
public User queryUserByName(String username);
}
创建service实现类
package com.jason.shiro.servce.impl;
import com.jason.shiro.entity.User;
import com.jason.shiro.mapper.UserMapper;
import com.jason.shiro.servce.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @authot jason_yan
* @date 2020/6/17-15:35
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByName(String username) {
return userMapper.queryUserByName(username);
}
}
实现Realm从数据库中查找用户信息
@Autowired
UserService userService;
...
// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证AuthenticationInfo");
// token是从controller中传递过来的,在此处实现真正的登录操作
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 在数据库中获取用户名和密码
User user = userService.queryUserByName(userToken.getUsername());
// 判断从前端输入的用户名与数据库中用户名是否匹配
if (user == null){
return null; // UnknownAccountException
}
// 密码认证,使用shiro的SimpleAuthenticationInfo实现
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo("", user.getPassword(), "");
return info;
}
Shiro授权操作
在ShiroConfig类的getShiroFilterFactoryBean()方法中,对特定页面添加字段(权限)
针对权限匹配:
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
...
// 拦截部分页面
filterMap.put("/index","anon");
filterMap.put("/add","authc");
filterMap.put("/update","authc");
// 授权:指定角色的访问权限,如果没有权限的话会跳转至未授权页面
filterMap.put("/add","perms[user:add]"); // 若用户含拥有"user:add"字段,则可以访问add页面
filterMap.put("/update","perms[user:update]");// 若用户含拥有"user:update"字段,则可以访问update页面
filterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 如果没有认证(即没有登录),则进行登录请求
filterFactoryBean.setLoginUrl("/login");
// 设置没有权限的用户 越权后跳转的页面
filterFactoryBean.setUnauthorizedUrl("/unauthc");
return filterFactoryBean;
}
在控制器中设置 当用户权限不足时的跳转页面
@GetMapping("/unauthc")
public String unAuthc(){
return "unauthc";
}
创建 unauthc.html 页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>您当前权限不足以访问此功能</h1>
<a th:href="@{/index}">返回主页</a>
</body>
</html>
在Realm的doGetAuthorizationInfo()方法中对用户添加权限addStringPermission()
package com.jason.shiro.config;
import com.jason.shiro.entity.User;
import com.jason.shiro.servce.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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @authot jason_yan
* @date 2020/6/17-11:17
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
// 授权操作
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权AuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 获取当前登录对象
Subject subject = SecurityUtils.getSubject();
// 得到User对象
User currentUser = (User) subject.getPrincipal();
if (currentUser.getUsername().equals("zs")){
info.addStringPermission("user:add");
return info;
}else if (currentUser.getUsername().equals("ls")){
info.addStringPermission("user:update");
return info;
}else {
return null;
}
}
// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), "");
return info;
}
}
当zs登录时,赋予张三user:add字段,当ls登录时,赋予李四user:update字段,然后在过滤器中匹配相应的权限
对死代码进行改造:除了根据字段对用户进行授权操作外,和可以根据身份角色进行授权
在数据库 tb_user表中添加roles属性(也可以添加perms属性-其值为user:add/user:update):表名每个人对应的角色
其中 admin … user:admin、zs … user:add、ls … user:update
同时为entity包中 User对象添加属性
private String role;
改造Realm中的死代码
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
...
// 获取当前登录对象
Subject subject = SecurityUtils.getSubject();
// 得到User对象
User currentUser = (User) subject.getPrincipal();
if (currentUser != null){
info.addRole(currentUser.getRole());
return info;
}else {
return null;
}
...
}
在ShiroConfig中改造拦截器
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
...
// 授权:指定角色的访问权限,如果没有权限的话会跳转至未授权页面
filterMap.put("/add","roles[add]"); // 用户的角色认证
filterMap.put("/update","roles[update]");
...
}
Shiro整合Thymeleaf
当张三登录时,只显示在自己权限范围内的功能页面,李四也是如此
Shiro整合Thymeleaf需要导入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
在ShiroConfig类中添加整合thymeleaf的Bean
// 整合ShiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
将当前用户登录的状态放入session中:如果有用户登录则加入session
在Realm认证(登录)操作中添加
// 认证操作
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...
// 将当前用户信息放入session中,供前端 获取用户在线状态
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser",user);
// 密码认证,使用shiro的SimpleAuthenticationInfo实现
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), "");
return info;
}
修改 index.html 主页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<hr>
<p th:text="${msg}"></p>
<p th:if="${#strings.isEmpty(session.loginUser)}">
<a th:href="@{/do/login}">登录</a>
</p>
<p>
<div shiro:hasRole="add">
<a th:href="@{/add}">add something</a>
</div>
<div shiro:hasRole="update">
<a th:href="@{/update}">update something</a>
</div>
</p>
</body>
</html>
退出登录操作
在ShiroConfig 过滤器中添加
filterMap.put("/logout",“logout”);
添加退出登录的请求按钮及链接
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<hr>
<p th:text="${msg}"></p>
<p th:if="${#strings.isEmpty(session.loginUser)}">
<a th:href="@{/do/login}">登录</a>
</p>
<p>
<div shiro:hasRole="add">
<a th:href="@{/logout}">退出登录</a> |
<a th:href="@{/add}">add something</a>
</div>
<div shiro:hasRole="update">
<a th:href="@{/logout}">退出登录</a> |
<a th:href="@{/update}">update something</a>
</div>
</p>
</body>
</html>