Springboot 集成 Shiro 入门学习

Shiro概述

主要特征

img

Shiro提供了如上图所示的特性,其中主要特性(其开发团队称之为应用安全的四大基石)如下:

  • Authentication - 身份认证 (与登陆相关,确定用户是谁)
  • Authorization - 确认权限 (确定用户能访问什么)
  • Session Management - 会话管理
  • Cryptography - 数据加密

shiro如何工作

img

应用代码的交互对象是 “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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值