(二十三)Spring Boot 整合 Shiro

1.Shiro 简介

        Apache Shiro 是一个开源的轻量级的 Java 安全框架,它提供身份验证、授权、密码管理以及会话管理等功能。 相对 Spring Security, Shiro 框架更加直观、易用,同时也能提供健壮的安全性。在传统的 SSM 框架中,手动整合 Shiro 的配置步骤还是比较多的,针对 Spring Boot, Shiro 官方提供了 shiro-spring-boot-web-starter用来简化 Shiro。

2.整合 Shiro

2.1 创建项目,添加 Shiro 依赖以及页面模板依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring-boot-web-starter</artifactId>
   <version>1.4.0</version>
</dependency>

<dependency>
   <groupId>com.github.theborakompanioni</groupId>
   <artifactId>thymeleaf-extras-shiro</artifactId>
   <version>2.0.0</version>
</dependency>

注意这里不需要添加 spring-boot-starter-web 依赖, shiro-spring-boot-web-starter 中已经依赖了spring-boot-starter-web。 同时,本案例使用 Thymeleaf 模版,因此添加 Thymeleaf 依赖,另外,为了在 Thymeleaf 中使用 shiro ,因此引 入了 thymeleaf-extras-shiro 依赖。

2.2 Shiro 基本配置

2.2.1 application properties配置文件

# 开启 Shiro 配置,默认为 true
shiro.enabled=true
# 开启 Shiro Web 配置,默认为 true
shiro.web.enabled=true
# 表示登录地址,默认为"/login.jsp"
shiro.loginUrl=/login
# 表示登录成功地址,默认为"/"。
shiro.successUrl=/index
# 表示未获授权默认跳转地址。
shiro.unauthorizedUrl=/unauthorized
# 表示是否允许通过 URL 参数实现会话跟踪, 如果网站支持 Cookie ,可以关闭此选项,默认为 true
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
# 表示是否允许通过 Cookie 实现会话跟踪,默认为 true
shiro.sessionManager.sessionidCookieEnabled=true

2.2.2 Java配置文件

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {


    @Bean
    public Realm realm() {
        TextConfigurationRealm realm = new TextConfigurationRealm();
        realm.setUserDefinitions("weigang=123,user\n admin=123,admin");
        realm.setRoleDefinitions("admin=read,write\n user=read");
        return realm;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/doLogin", "anon");
        chainDefinition.addPathDefinition("/logout", "logout");
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}
  • 这里提供两个关键Bean ,一个是Realm ,另一个是ShiroFilterChainDefinition。至于 ShiroDialect, 则是为了支持在 Thymeleaf 中使用 Shiro 标签,如果不在 Thymeleaf 中使用 Shiro 标签,那么可以不提供 ShiroDialect。
  • Realm 可以是自定义 Realm ,也可以是 Shiro 提供的 Realm, 简单起见,本案例没有配置数据库连接,这里直接配置了两个用户: weigang/123 和 admin/123 ,分别对应角色 user 和 admin, user 具有 read 权限,admin 则具有 read 和 write 权限。
  • ShiroFilterChainDefinition Bean 中配置了基本的过滤规则,/logindoLogin可以匿名访问,/logout是一个注销登录请求,其余请求则都需要认证后才能访问。

接下来配置登录接口以及页面访问接口,代码如下:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

    @PostMapping("/doLogin")
    public String doLogin(String username, String password, Model model) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            model.addAttribute("error", "用户名或密码输入错误!");
            return "login";
        }
        return "redirect:/index";
    }

    @RequiresRoles("admin")
    @GetMapping("/admin")
    public String admin() {
        return "admin";
    }

    @RequiresRoles(value = {"admin", "user"}, logical = Logical.OR)
    @GetMapping("/user")
    public String user() {
        return "user";
    }
}
  • 在doLogin 方法中,首先构造一个 UsemamePasswordToken 实例 ,然后获取一个 Subject 对象并调用该对象中的 login 方法执行登录操作,在登录操作执行过程中,当有异常抛出时,说明登录失败,携带错误信息返回登录视图;当登录成功时,则重定向到/index
  • 接下来暴露两个接口/admin/user,对于/admin接口,需要具有 admin 角色才可以访问;对于/user接口,具备 admin 角色或者 user 角色即可访问。

对于其他不需要角色就能访问的接口,直接在 WebMvc 中配置即可,代码如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/unauthorized").setViewName("unauthorized");
    }
}

接下来创建全局异常处理器进行全局异常处理,本案例主要是处理授权异常,代码如下:

import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(AuthorizationException.class)
    public ModelAndView error(AuthorizationException e) {
        ModelAndView mv = new ModelAndView("unauthorized");
        mv.addObject("error", e.getMessage());
        return mv;
    }
}

当用户访问未授权的资源时 ,跳转到 unauthorized 视图中,井携带出错信息。

2.3 html页面创建

2.3.1 index.html

<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>Hello, <shiro:principal/></h3>
<h3><a href="logout">注销登录</a></h3>
<h3><a shiro:hasRole="admin" href="/admin">管理员页面</a></h3>
<h3><a shiro:hasAnyRoles="admin,user" href="/user">普通用户页面</a></h3>
</body>
</html>

index.html 是登录成功后的首页,首先展示当前登录用户的用户名,然后展示一个“注销登录”链接,若当前登录用户具备 admin 角色,则展示一个“管理员页面” 的超链接; 若用户具备“admin ” 或者“ user ”角色 ,则 展示一个“普通用户页面”的超链接。注意这里导入的名称空间是 xmlns:shiro=“http://www.pollix.at/thymeleaf/shiro” ,和 JSP 中导入的 Shiro 名称空间不一致。

2.3.2 login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <form action="/doLogin" method="post">
        <input type="text" name="username"></br>
        <input type="password" name="password"><br>
        <div th:text="${ error }"></div>
        <input type="submit" value="登录">
    </form>
</div>
</body>
</html>

2.3.3 admin.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>管理员页面</h1>
</body>
</html>

2.3.4 user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>普通用户页面</h1>
</body>
</html>

2.3.5 unauthorized.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>未获授权,非法访问</h3>
<h3 th:text="${ error }"></h3>
</body>
</html>

2.4 测试

配置完成后,启动 Spring Boot 项目,访问登录页面http://localhost:8080/login,分别使用 weigang/123 和admin/123 登录,结果如图所示。注意,因为 weigang 用户不具备 admin 角色,因此登录成功后的页面上没有前往管理员页面的超链接。
在这里插入图片描述
登录成功后,无论是 weigang 还是 admin 用户,单击“注销登录”都会注销成功,然后回到登录页面, weigang 用户因为不具备 admin 角色,因此没有“管理员页面”的超链接,无法进入管理员页面中,此时,若用户使用 weigang 用户登录,然后手动在地址栏输入 http://localhost:8080/admin ,则会跳转到未授权页面。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值