springboot结合shiro实战-身份认证

news2025/1/15 16:35:06

 

目录

环境搭建

配置shiro环境

总结


hello,大家好,我们写任何企业级项目基本都会需要做权限,权限包含身份认证和授权。

所谓身份认证,就是证明你是你。

所谓授权就是明白你登录之后能干什么。

现在,让我们用springboot项目入手,结合shiro框架来完成这一切。

环境搭建

基本信息:

springboot版本:

初始其他依赖:

创建完毕:

添加Shiro和Spring Boot的依赖项。在你的pom.xml文件中添加以下依赖项:

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

配置shiro环境

第一步,创建一个Shiro的配置类。

创建一个类,并使用@Configuration注解标记它。在该类中,你可以配置Shiro的相关设置,例如Realm、Session管理器等。

配置代码如下:

package com.it.shirodemo.config;

import com.it.shirodemo.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }


    @Bean(name = "mySecurityManager")
    public DefaultWebSecurityManager  securityManager(@Qualifier("myRealm") MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    /**
     * 路径过滤规则
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        //登录不需要校验权限
        chainDefinition.addPathDefinition("/login", "anon");
        //其他任何url都需要校验是否登录
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("mySecurityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
        return filterFactoryBean;
    }

    /**
     * 开启Shiro注解模式,可以在Controller中的方法上添加注解
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("mySecurityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

这些都是比较固定的代码,你甚至都不需要知道每个bean的含义,直接拷贝过去用,也是没有问题的。

然后,我们需要自定义一个Realm。

代码

package com.it.shirodemo.realm;

import com.it.shirodemo.entity.User;
import com.it.shirodemo.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 javax.annotation.Resource;


public class MyRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 实现授权逻辑
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获得当前subject
        Subject subject = SecurityUtils.getSubject();
        //获得当前的principal,也就是认证完后我们放入的信息
        User currentUser = (User) subject.getPrincipal();
        //添加权限
        info.addStringPermissions(currentUser.getPerms());

        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 实现认证逻辑
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //从数据库中查询该用户
        String username = usernamePasswordToken.getUsername();
        User user = userService.queryUserByName(username);
        //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
        if (user == null) {
            return null;
        }
        // 第一个参数为principal,就是授权方法里面拿到的那个对象;
        // 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
        // 第三个参数为 realmName
        return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
    }
}

主要是写授权和认证的方法,上面的 doGetAuthorizationInfo 用来授权,在你每一次访问系统的某个url时会调用,目标是获取当前用户的权限。

看下用户类:

代码

package com.it.shirodemo.entity;

import lombok.Data;
import java.util.Set;

@Data
public class User {
    private String userName;
    private String pwd;
    private Set<String> perms;
}

perms是一个Set集合,我们在模拟的service中进行初始化和查询:

package com.it.shirodemo.service;

import com.it.shirodemo.entity.User;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserService {


    public User queryUserByName(String username) {
        List<User> users = new ArrayList<>();
        users.add(new User(){{
            setUserName("admin");
            setPwd("123");
            setPerms(new HashSet<String>(){{
                add("shiro:user-query");
                add("shiro:user-add");
                add("shiro:user-delete");
                add("shiro:user-edit");
            }});
        }});

        users.add(new User(){{
            setUserName("zhangsan");
            setPwd("123");
            setPerms(new HashSet<String>(){{
                add("shiro:user-query");
            }});
        }});
        List<User> userList = users.stream().filter(e -> e.getUserName().equals(username)).collect(Collectors.toList());
        if(userList.size() > 0){
            return userList.get(0);
        }
        return null;

    }
}

为了测试,我们专门弄了一个用户的Controller

package com.it.shirodemo.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/user/")
public class UserController {

    @GetMapping("/query")
    @RequiresPermissions("shiro:user-query")
    public String query(){
       return "用户查询";
    }

    @GetMapping("/edit")
    @RequiresPermissions("shiro:user-edit")
    public String edit(){
        return "用户修改";
    }

    @GetMapping("/delete")
    @RequiresPermissions("shiro:user-delete")
    public String delete(){
        return "用户删除";
    }

    @GetMapping("/add")
    @RequiresPermissions("shiro:user-add")
    public String add(){
        return "用户新增";
    }
}

我们期望,admin用户可以访问所有的权限,zhangsan只能访问用户查询的接口。

执行完

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    // 实现授权逻辑
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //获得当前subject
    Subject subject = SecurityUtils.getSubject();
    //获得当前的principal,也就是认证完后我们放入的信息
    User currentUser = (User) subject.getPrincipal();
    //添加权限
    info.addStringPermissions(currentUser.getPerms());

    return info;
}

这个授权方法后,返回的授权信息对象就有所有的权限列表,如果包含了你本次访问的权限,比如 shiro:user-add ,就允许访问,否则不允许访问。

再来看认证方法,认证说白了就是登录验证。

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // 实现认证逻辑
    UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
    //从数据库中查询该用户
    String username = usernamePasswordToken.getUsername();
    User user = userService.queryUserByName(username);
    //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
    if (user == null) {
        return null;
    }
    // 第一个参数为principal,就是授权方法里面拿到的那个对象;
    // 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
    // 第三个参数为 realmName
    return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
}

这里有个很有意思的东西,最终return出去的SimpleAuthenticationInfo对象,构造方法第一个参数是一个Object类型。

这个玩意其实就是:

也就是说,你在认证方法里面传递什么参数,授权方法里面获取的principal就是什么对象。

为什么要提供这么一个口子让你传递object呢,其实就是为了让你在授权的时候能获取当前用户的授权列表啊!

我看到网上很多讲shiro的文章,对于这一点的理解比较模糊,反正你就记住,这个参数你不管传什么,一定要能够通过它来获取权限列表。比如,我这里就传一个User对象,user对象里面已经有perms了,那就没问题。你也可以传username,然后在授权的时候去数据库查询权限列表,也完全没问题。

最后,再给出登录的方法:

package com.it.shirodemo.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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

    @GetMapping("/login")
    public String login(String username,String password){
        Subject subject = SecurityUtils.getSubject();
        //令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            //登录认证
            subject.login(token);
            return "恭喜,登录成功!";
        }catch (UnknownAccountException e){
            return "账号不存在";
        }catch (IncorrectCredentialsException e){
            return "密码错误";
        }

    }
}

验证,打开浏览器,因为是Get请求,我们直接用浏览器来发。

http://localhost:8080/login?username=admin&password=123

然后去访问:http://localhost:8080/v1/user/add

如果是张三登录,访问http://localhost:8080/v1/user/add

后台报错了:

org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method: public java.lang.String com.it.shirodemo.controller.UserController.add()

总结

本文实现了所谓ACL的权限控制,用shiro框架结合springboot实现,非常适合初学者学习。

源码下载 https://gitee.com/skyblue0678/shiro-demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/885442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

根据一棵树的两种遍历构造二叉树

题目 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,…

【Java从0到1学习】09 正则表达式

1. 正则表达式概述 在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 正则表达式&#xff0c;又称正规表示法、常规表示法&#xff…

js watermark实现水印效果

2023.8.15今天学习了如何给页面添加水印&#xff0c;效果如下&#xff1a; 1.创建watermark.js文件&#xff1a; import id from "element-ui/src/locale/lang/id";let watermark {} let setWatermark (str) > {let id 1.23452384164.123412415if (document.g…

智安网络|深入比较:Sass系统与源码系统的差异及选择指南

随着前端开发的快速发展&#xff0c;开发人员需要使用更高效和灵活的工具来处理样式表。在这个领域&#xff0c;Sass系统和源码系统是两个备受关注的选项。 Sass系统 Sass&#xff08;Syntactically Awesome Style Sheets&#xff09;是一种CSS预处理器&#xff0c;它扩展了CS…

管理类联考——逻辑——真题篇——按知识分类——汇总篇——一、形式逻辑——三段论——第一节——推结论

文章目录 第一节 三段论-推结论题-三段论-结构-推结论&#xff1a;所有A是B&#xff1b;所有B是C&#xff1b;得&#xff1a;所有A是C。&#xff08;最简单的模型&#xff09;真题&#xff08;2011-27&#xff09;-三段论-推结论真题&#xff08;2011-28&#xff09;-三段论-推…

react-router v6版本使用记录

1&#xff1a;首先安装依赖 最基本的使用&#xff0c;引入相关依赖&#xff0c;开箱即用 import { BrowserRouter, Link, Routes, Route } from "react-router-dom" import Home from "./home" import About from "./about"function App () {// …

MapBox历史轨迹

成果实例 思路 思路就是取从起点开始一路setdata吧 分三个图层&#xff0c;一个路径图层&#xff0c;一个飞机图层&#xff0c;一个显示名字的图层&#xff0c;遍历路径图层的点&#xff0c;经过显示名字图层的时候就显示图层&#xff0c;飞机图层的点和角度动态计算&#xff…

COMSOL电磁仿真-网格剖分-边界层

COMSOL电磁仿真-网格剖分-边界层 在低频磁场中&#xff0c;随着磁场的频率增加&#xff0c;金属的集肤效应会逐渐显著&#xff0c;为了解析趋肤层&#xff0c;需要对金属的表面进行网格加密&#xff0c;这一过程主要通过剖分边界层实现&#xff0c;本文分为两部分&#xff0c;第…

【LeetCode】337.打家劫舍Ⅲ

题目 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直…

2023年软件测试岗面试题+答案汇总,也许你例offer就差这一个...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、B/S架构和C/S架…

16.5.4 【Linux】SELinux 政策内的规则管理

SELinux 各个规则的布林值查询 getsebool 如果想要查询系统上面全部规则的启动与否 &#xff08;on/off&#xff0c;亦即布林值&#xff09;&#xff0c;很简单的通过 sestatus-b 或 getsebool -a 均可&#xff01; SELinux 各个规则规范的主体程序能够读取的文件 SELinux typ…

flex布局 vs grid布局

问题&#xff1a;一行最多显示3个item&#xff0c;实现最后一行居左 <div class"chart-wrap"><div class"chart-item">图</div><div class"chart-item">图</div><div class"chart-item">图</…

医生如何使用ChatGPT提高工作效率

文章目录 引言案例一&#xff1a;快速获取医学知识案例二&#xff1a;协助患者自我诊断案例三&#xff1a;辅助临床决策案例四&#xff1a;提供在线咨询服务案例五&#xff1a;用病人易于理解的语言总结关键的临床信息案例六&#xff1a;高效整理医疗文件案例七&#xff1a;根据…

报修管理系统的功能有多强大?的修报修管理系统有什么优势?

报修管理系统是一种功能强大的系统&#xff0c;可帮助企业快速响应设备故障和异常情况&#xff0c;并将实时信息反馈给有关人员&#xff0c;以及进行准确的统计分析。通过使用报修管理系统&#xff0c;企业能够极大地提升工作效率&#xff0c;优化运营流程&#xff0c;减少人员…

记一次文件因content-type问题无法打开的经历

记一次文件因content-type问题无法打开的经历 引场景方案Jsoup的Content-Typesimplemagicfile cant open流不可重复消费问题Tika 总结 引 在Http请求头和响应头都有这个attribute&#xff0c;来声明请求和响应报文的资源类型。 Content-Type&#xff08;MediaType&#xff09;…

你真的了解数据结构与算法吗?

数据结构与算法&#xff0c;是理论和实践必须紧密结合的一门学科&#xff0c;有关数据结构和算法同类的课程或书籍&#xff0c;有些只是名为“数据结构”&#xff0c;而非“数据结构与算法”&#xff0c;它们在内容上并无很大区别。 实际上&#xff0c;数据结构和算法&#xf…

有哪些前端开发工具推荐? - 易智编译EaseEditing

在前端开发中&#xff0c;有许多工具可以帮助你更高效地进行开发、调试和优化。以下是一些常用的前端开发工具推荐&#xff1a; 代码编辑器/集成开发环境&#xff08;IDE&#xff09;&#xff1a; Visual Studio Code&#xff1a;功能强大、轻量级的代码编辑器&#xff0c;支…

富士胶片 : 柯达倒下了,我们凭什么屹立不倒

当整个行业被新技术颠覆后&#xff0c;其中的领军企业如何才能活下去? 富士胶片在面临行业被颠覆的情况下&#xff0c;迅速调整策略&#xff0c;果断裁减传统业务、大规模投资新兴领域&#xff0c;体现出敏捷的链节竞争优势。这使得富士胶片在数码时代来临之际&#xff0c;能…

MATLAB高分辨率图片

把背景调黑&#xff0c;把曲线调黄&#xff0c;把grid调白&#xff0c;调调字体字号的操作 close all a0:0.1:10; noise2*rand(1,length(a)); bsin(a)sin(3*a)noise;plot(a,b,y,linewidth,2); ylim([-3 4]) %y轴范围 set(gca,xgrid,on,ygrid,on,gridlinestyle,-,Grid…

01.在实战中提升自己----表达式解析

1.我们面临的问题与挑战 我的工作成功就是交付可用产品&#xff0c;而且是要满足超大规模企业应用的产品。在实践过程中&#xff0c;不管我们是处于哪个阶段&#xff0c;交付的内容就是会大规模应用的工具。在我们的面前&#xff0c;要不提供完善的支持配套&#xff0c;要不投…