Shiro是一个强大且易用的Java安全框架,能够提供身份验证、授权、加密和会话管理等功能。Spring Boot则是一个用于快速搭建Spring应用的框架,简化了Spring应用的开发过程。将Shiro与Spring Boot集成,可以为Spring Boot应用添加强大的安全功能。下面将详细介绍Shiro与Spring Boot的集成指南。

环境准备

在开始集成之前,需要确保已经安装了Java开发环境(JDK 8及以上)和Maven。创建一个新的Spring Boot项目,可以使用Spring Initializr(https://start.spring.io/ )快速生成项目骨架,选择所需的依赖,如Spring Web。

添加Shiro依赖

在项目的pom.xml文件中添加Shiro与Spring Boot集成的依赖。以下是所需的依赖配置:

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Shiro Spring Boot Starter -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-web-starter</artifactId>
        <version>1.9.1</version>
    </dependency>
</dependencies>

添加完依赖后,Maven会自动下载并管理这些依赖。

配置Shiro

创建一个Shiro配置类,用于配置Shiro的相关组件,如Realm、SecurityManager等。以下是一个示例配置类:

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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 ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        return shiroFilterFactoryBean;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        // 配置哪些请求需要进行权限验证
        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/", "authc");
        return chainDefinition;
    }
}

在上述配置中,shiroFilterFactoryBean方法用于创建Shiro的过滤器工厂,设置了登录页面和未授权页面。shiroFilterChainDefinition方法用于定义请求的过滤规则,这里配置了/login请求不需要进行身份验证,其他请求都需要进行身份验证。

创建自定义Realm

Realm是Shiro进行身份验证和授权的核心组件,需要创建一个自定义的Realm类来实现身份验证和授权逻辑。以下是一个示例:

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;

public class CustomRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 模拟添加角色和权限
        authorizationInfo.addRole("admin");
        authorizationInfo.addStringPermission("user:manage");
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());

        // 模拟验证
        if (!"admin".equals(username)) {
            throw new UnknownAccountException("用户名不存在");
        }
        if (!"123456".equals(password)) {
            throw new IncorrectCredentialsException("密码错误");
        }

        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

在CustomRealm类中,doGetAuthorizationInfo方法用于进行授权操作,这里模拟添加了一个admin角色和user:manage权限。doGetAuthenticationInfo方法用于进行身份验证,验证用户名和密码是否正确。

将自定义Realm注入到SecurityManager

需要将自定义的Realm注入到SecurityManager中,修改ShiroConfig类如下:

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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 CustomRealm customRealm() {
        return new CustomRealm();
    }

    @Bean
    public SecurityManager securityManager(CustomRealm customRealm) {
        org.apache.shiro.mgt.DefaultWebSecurityManager securityManager = new org.apache.shiro.mgt.DefaultWebSecurityManager();
        securityManager.setRealm(customRealm);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        return shiroFilterFactoryBean;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        // 配置哪些请求需要进行权限验证
        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/", "authc");
        return chainDefinition;
    }
}

在上述代码中,customRealm方法创建了自定义的Realm实例,securityManager方法将自定义的Realm注入到SecurityManager中。

创建控制器

创建控制器来处理登录、未授权等请求。以下是一个示例:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String loginPage() {
        return "login";
    }

    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            currentUser.login(token);
            return "redirect:/home";
        } catch (UnknownAccountException | IncorrectCredentialsException e) {
            return "redirect:/login?error=1";
        } catch (AuthenticationException e) {
            return "redirect:/login?error=2";
        }
    }

    @GetMapping("/home")
    public String homePage() {
        return "home";
    }

    @GetMapping("/unauthorized")
    public String unauthorizedPage() {
        return "unauthorized";
    }
}

在上述控制器中,loginPage方法用于返回登录页面,login方法用于处理登录请求,根据登录结果进行相应的跳转。homePage方法用于返回主页,unauthorizedPage方法用于返回未授权页面。

创建视图页面

在src/main/resources/templates目录下创建登录页面login.html、主页home.html和未授权页面unauthorized.html。以下是一个简单的登录页面示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form action="/login" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>


        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>


        <input type="submit" value="Login">
    </form>
    <% if (request.getParameter("error") != null) { %>
        <p style="color: red;">Invalid username or password<% } %>
</body>
</html>

其他页面可以根据需要进行简单的设计。

测试集成效果

启动Spring Boot应用,访问应用的根路径,会自动跳转到登录页面。输入正确的用户名和密码(这里是admin和123456),点击登录按钮,会跳转到主页。如果输入错误的用户名或密码,会显示错误信息。如果访问需要权限的页面而没有相应的权限,会跳转到未授权页面。

通过以上步骤,就完成了Shiro与Spring Boot的集成。在实际项目中,可以根据需求进一步扩展和优化Shiro的配置,如使用数据库进行用户信息和权限信息的管理,实现更复杂的授权逻辑等。