SpringBoot——整合Shiro,实现安全认证和权限管理功能

目录

Shiro

项目总结

新建一个SpringBoot项目

pom.xml

application.properties(配置文件)

User(实体类)

UserMapper(数据访问层接口)

UserMapper.xml(数据库映射文件)

UserService(服务层接口)

UserServiceImpl(接口实现类)

UserRealm(认证、授权逻辑代码类)

ShiroConfig(Shiro配置类)

ShiroApplication(启动类)

index.html(主页面)

login.html(登录页面)

add.html(添加页面)

delete.html(删除页面)

项目测试


Shiro

  • Apache Shiro是一个开源的轻量级的Java安全框架,它提供身份验证、授权、密码管理以及会话管理等功能。相对于Spring Security,Shiro框架更加直观、易用,同时也能提供健壮的安全性
  • 在实际工作时可能使用小而简单的Shiro就足够了,不存在Shiro和Security哪个更好
  • Shiro支持的功能:
    • Authentication:用户身份认证、登录,验证用户是否拥有相应的身份
    • Authorization:授权,验证某个已认证的用户是否拥有某个权限
    • Session Management:会话管理,用户登录后就是第一次会话,在没有退出之前,用户的所有信息都在会话中
    • Cryptography:加密,保证数据的安全性,如将密码加密存储到数据库中
    • Web Support:Web支持,使系统 可以非常容易地集成到Web环境中
    • Caching:缓存,比如用户登录后,其用户信息,以及拥有的角色、权限不必每次都去查,这样可以提高效率
    • Concurrency:Shiro支持多线程应用的并发验证,如在一个线程中开启另一个线程,能把权限自动地传播过去
    • Testing:提供测试支持
    • Run As:允许一个用户冒用另一个用户的身份(如果他们允许)进行访问
    • Remember Me:一次登录后,之后登录不用输入用户名和密码

项目总结

  • 该项目是SpringBoot整合Shiro实现了用户管理系统中的登录认证和授权

  • 需求分析: 首先,明确项目的安全需求,包括用户认证、权限管理、会话管理等方面的具体要求。了解需要保护的资源和不同用户角色的访问权限。

  • 项目初始化: 使用 Spring Initializr 或其他工具创建一个新的 Spring Boot 项目。配置基本的项目结构和依赖管理(Maven 或 Gradle)。

  • 引入依赖: 在项目的 pom.xml 文件中添加 Shiro 的依赖项

  • 配置 Shiro: 创建一个 Shiro 配置类,用于配置安全管理器、Realm 和拦截器链等。

  • 实现 Realm: 创建自定义的 Realm 类,用于处理用户认证和授权逻辑。

  • 编写登录和权限控制代码: 创建控制器来处理用户登录和注销请求。

  • 测试与调试: 运行项目并进行各种测试,确保用户认证和授权功能正常工作。

新建一个SpringBoot项目

新建数据库、表

CREATE DATABASE shiro;
USE shiro;
CREATE TABLE IF NOT EXISTS `t_user`(
	`id` INT PRIMARY KEY COMMENT '用户ID(主键)',
    `username` VARCHAR(255) UNIQUE NOT NULL COMMENT '用户名',
    `password` VARCHAR(20) NOT NULL COMMENT '登录密码',
    `permissions` VARCHAR(100) NOT NULL COMMENT '用户权限'
)COMMENT='用户信息表';
INSERT INTO t_user(id,username,password,permissions) VALUE (1,'zhangsan','123','user:delete');
INSERT INTO t_user(id,username,password,permissions) VALUE (2,'lisi','123','user:add');

项目结构:

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.study</groupId>
	<artifactId>shiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>shiro</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		<!--MyBatis逆向工程-->
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-maven-plugin</artifactId>
			<version>1.3.2</version>
		</dependency>

		<!--@Data注解-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<!--数据库-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>

        <!--为了在Thymeleaf中使用shiro标签,所以引入 thymeleaf-extras-shiro依赖-->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>2.1.0</version>
		</dependency>

		<!--shiro安全认证框架-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring-boot-web-starter</artifactId>
			<version>1.9.0</version>
		</dependency>

		<!--使用.html模板-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

application.properties(配置文件)

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

# MySQL数据库的配置信息
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# mybatis的配置信息
# .xml文件放置处,此处指resources文件夹下
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.study.shiro.entity

User(实体类)

package com.study.shiro.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
    private String permissions;
}

UserMapper(数据访问层接口)

package com.study.shiro.mapper;

import com.study.shiro.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface UserMapper{
    public User findUserByName(String username);
}

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.study.shiro.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="com.study.shiro.entity.User">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="username" column="username" jdbcType="VARCHAR"/>
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="permissions" column="permissions" jdbcType="VARCHAR"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,username,password,
        permissions
    </sql>
    <select id="findUserByName" resultType="com.study.shiro.entity.User">
        select * from t_user where username like #{username}
    </select>
</mapper>

UserService(服务层接口)

package com.study.shiro.service;

import com.study.shiro.entity.User;

public interface UserService{
    public User findUserByName(String username);
}

UserServiceImpl(接口实现类)

package com.study.shiro.service.impl;

import com.study.shiro.entity.User;
import com.study.shiro.mapper.UserMapper;
import com.study.shiro.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public User findUserByName(String username) {
        return userMapper.findUserByName(username);
    }
}




UserRealm(认证、授权逻辑代码类)

package com.study.shiro.config;

import com.study.shiro.entity.User;
import com.study.shiro.service.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;

/**
 * 实现用户登录认证逻辑
 */
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    /**
     * 用户授权的逻辑代码
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了===>用户授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        //获取User对象
        User currentUser = (User) subject.getPrincipal();
        //设置权限
        info.addStringPermission(currentUser.getPermissions());

        return info;
    }

    /**
     * 用户认证的逻辑代码
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了===>用户认证");
        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
        //连接真实的数据库,调取用户信息
        User user = userService.findUserByName(token.getUsername());
        //该用户不存在
        if(user==null){
            return null;
        }
        Subject subject = SecurityUtils.getSubject();
        //将登录用户放入Session中
        subject.getSession().setAttribute("loginUser",user);
        //密码认证
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

ShiroConfig(Shiro配置类)

package com.study.shiro.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * ShiroDialect类是为了支持在Thymeleaf中使用的Shiro标签
     */
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

        // 设置安全管理器
        bean.setSecurityManager(securityManager);

        // Shiro内置过滤器,可以实现权限相关的拦截器
        /*
            常用的过滤器:
                anon: 无需认证(登录)可以访问
                authc: 必须认证才可以访问
                user: 如果使用rememberMe的功能可以直接访问
                perms: 该资源必须得到资源权限才可以访问
                role: 该资源必须得到角色权限才可以访问
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/login", "anon");
        filterMap.put("/index", "anon");
        filterMap.put("/doLogin", "anon"); // 无需认证即可访问
        filterMap.put("/logout", "logout"); // Shiro自带的退出登录
        filterMap.put("/**", "authc"); // 拦截其他所有请求,需要认证
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/delete","perms[user:delete]");

        // 设置默认登录的 URL,身份认证失败会访问该 URL
        bean.setLoginUrl("/login");
        // 设置登录成功后要跳转的链接
        bean.setSuccessUrl("/index");
        //未授权时跳转的页面
        bean.setUnauthorizedUrl("/noauth");
        bean.setFilterChainDefinitionMap(filterMap);

        return bean;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联Realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Realm对象
     */
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

UserController(控制器)

package com.study.shiro.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

    //主页面
    @RequestMapping({"/index","/"})
    public String index(Model model){
        model.addAttribute("msg","hello shiro!");
        return "index";
    }

    //登录页面
    @RequestMapping("/login")
    public String login(){
        return "login";
    }

    //处理登录请求,是否成功
    @RequestMapping("/doLogin")
    public String doLogin(String username,String password,Model model){
        //封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //获取当前用户
        Subject currentUser = SecurityUtils.getSubject();
        //执行登录的方法,只要没有异常就代表登录成功
        try {
            currentUser.login(token);
            return "index";
        } catch (UnknownAccountException uae) {
            model.addAttribute("msg","用户名不存在!");
            return "login";
        }catch (IncorrectCredentialsException ice){
            model.addAttribute("msg","密码错误!");
            return "login";
        }

    }

    //注销
    @RequestMapping("/logout")
    public String logout(){
        Subject currentUser = SecurityUtils.getSubject();
        currentUser.logout();
        return "index";
    }

    @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "未经授权不能访问此页面";
    }

    //获取指定授权后,可访问该页面
    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }

    //获取指定授权后,可访问该页面
    @RequestMapping("/user/delete")
    public String delete(){
        return "/user/delete";
    }
}

ShiroApplication(启动类)

package com.study.shiro;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ShiroApplication {

	public static void main(String[] args) {
		SpringApplication.run(ShiroApplication.class, args);
	}

}

index.html(主页面)

<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>主页面</title>
    <!--引入Bootstrap国内CDN库-->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <h1>主页面</h1>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <!--会话中没有用户,即用户未登录,显示"登录"超链接-->
            <p th:if="${session.loginUser == null}">
                <a th:href="@{/login}">登录</a>
            </p>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <!--会话中保存了用户,即用户已登录,显示"注销"超链接-->
            <p th:if="${session.loginUser != null}">
                <a th:href="@{/logout}">注销</a>
            </p>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <div shiro:haspermission="user:add">
                <a th:href="@{/user/add}">添加</a>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <div shiro:haspermission="user:delete">
                <a th:href="@{/user/delete}">删除</a>
            </div>
        </div>
    </div>
</div>
</body>
</html>

login.html(登录页面)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-4 col-md-offset-3">
                <h1>登录页面</h1>
            </div>
        </div>
        <div class="row">
            <div class="col-md-4 col-md-offset-3">
                <p style="color:red;" th:text="${msg}"></p>
            </div>
        </div>
        <form class="form-horizontal" th:action="@{/doLogin}" method="post">
            <div class="form-group">
                <label class="col-sm-2 control-label">用户名</label>
                <div class="col-sm-4">
                    <input type="text" class="form-control" name="username">
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">密码</label>
                <div class="col-sm-4">
                    <input type="password" class="form-control" name="password">
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="submit" class="btn btn-default">登录</button>
                </div>
            </div>
        </form>
    </div>
</body>
</html>

add.html(添加页面)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加页面</title>
    <!--引入Bootstrap国内CDN库-->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-5"><h1>添加</h1></div>
    </div>
</div>
</body>
</html>

delete.html(删除页面)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>删除页面</title>
    <!--引入Bootstrap国内CDN库-->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-5"><h1>删除</h1></div>
    </div>
</div>
</body>
</html>

项目测试

1、访问主页面:localhost:8080/index,点击“登录”

2、输入用户信息,登录

3、用户zhangsan拥有“删除”的权限

4、点击“注销”,返回用户登录页面

5、用户lisi,拥有“添加”的权限

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戏拈秃笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值