首页 > 其他分享 >springboot结合shiro实战-身份认证

springboot结合shiro实战-身份认证

时间:2023-10-15 12:32:43浏览次数:23  
标签:return springboot 认证 user org apache import shiro


springboot结合shiro实战-身份认证_spring boot

 

目录

环境搭建

配置shiro环境

总结


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

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

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

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

环境搭建

基本信息:

springboot结合shiro实战-身份认证_后端_02

springboot版本:

springboot结合shiro实战-身份认证_apache_03

初始其他依赖:

springboot结合shiro实战-身份认证_apache_04

创建完毕:

springboot结合shiro实战-身份认证_后端_05

添加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管理器等。

springboot结合shiro实战-身份认证_spring_06

配置代码如下:

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。

springboot结合shiro实战-身份认证_后端_07

代码

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时会调用,目标是获取当前用户的权限。

看下用户类:

springboot结合shiro实战-身份认证_spring_08

代码

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类型。

springboot结合shiro实战-身份认证_java_09

这个玩意其实就是:

springboot结合shiro实战-身份认证_apache_10

也就是说,你在认证方法里面传递什么参数,授权方法里面获取的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

springboot结合shiro实战-身份认证_spring_11

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

springboot结合shiro实战-身份认证_java_12

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

springboot结合shiro实战-身份认证_spring boot_13

后台报错了:

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

标签:return,springboot,认证,user,org,apache,import,shiro
From: https://blog.51cto.com/u_10957019/7871092

相关文章

  • Apache 安全框架Shiro
     ApacheShiro(发音为shee-roh,日语堡垒(Castle)的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障-从命令行应用、移动应用到大型网络及企业应用。相较于SpringSecurity来说较为简单,易于上手。可以非常容易的开发出足够好的应......
  • Springboot 加密方案探讨
    背景toB的本地化java应用程序,通常是部署在客户机器上,为了保护知识产权,我们需要将核心代码(例如Lience,Billing,Pay等)进行加密或混淆,防止使用jadx等工具轻易反编译。同时,为了更深层的保护程序,也要防止三方依赖细节被窥探;业界方案ProGuard简介:开源社区有名的免费混淆工具,......
  • 测试springboot项目苍穹外卖,解决websocket“服务器错误,无法接收实时报警信息”问题
    使用IDEA启动springboot项目苍穹外卖后,http://localhost:8071/能够正常访问登录,但是网页右上角始终显示“服务器错误,无法接收实时报警信息”: 在网上搜索找到:https://blog.csdn.net/qq_65032048/article/details/132077097,发现可能是修改了nginx端口号为8071导致。解决办法:在n......
  • springboot启动时缓存数据
    一、Emos系统的常量数据在sys_config数据表中保存了Emos系统的常量配置信息,其中就包括了考勤部分的常量信息。例如每天上班考勤从几点开始,截止到几点。下班考勤从几点开始,几点结束。 因为这些常量信息跟考勤模块息息相关,所以我们要编写Java代码,在SpringBoot项目启动的......
  • openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文
    openGauss学习笔记-99openGauss数据库管理-管理数据库安全-客户端接入认证之配置文件参考99.1参数说明表1参数说明参数名称描述取值范围local表示这条记录只接受通过Unix域套接字进行的连接。没有这种类型的记录,就不允许Unix域套接字的连接。只有在从服务器本机......
  • Springboot中使用RabbitMq
    代码地址:https://gitee.com/Aes_yt/middleware-demo/tree/master/rabbitmq安装RabbitMq1.docker拉取镜像dockerpullrabbitmq:3.9.29-management2.创建rabbitmq容器dockerrun-d--namerabbitmq-p5672:5672-p15672:15672rabbitmq:3.9.29-management3.访问地......
  • openGauss学习笔记-98 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置客
    openGauss学习笔记-98openGauss数据库管理-管理数据库安全-客户端接入认证之配置客户端接入认证98.1背景信息如果主机需要远程连接数据库,必须在数据库系统的配置文件中增加此主机的信息,并且进行客户端接入认证。配置文件(默认名称为pg_hba.conf)存放在数据库的数据目录里。hba(ho......
  • AP5125 DC-DC降压恒流IC SOT23-6 过认证 9-100V 6A电源驱动线路图
    1,产品描述 AP5125是一款外围电路简单的Buck型平均电流检测模式的LED恒流驱动器,适用于8-100V电压范围的非隔离式大功率恒流LED驱动领域。芯片采用固定频率140kHz的PWM工作模式,利用平均电流检测模式,因此具有优异的负载调整率特性,高精度的输出电流特性。AP51......
  • 采用SpringBoot+原生HTML+MySQL开发的电子病历系统源码
    电子病历系统采用“所见即所得、一体化方式”,协助医生和护士准确、标准、快捷实现病历书写、修改、审阅、打印、体温单浏览、医嘱管理等,是提供病历快速简洁化完成的一系列综合型医生病历工作平台。本套电子病历系统主要面向医疗机构医生、护士,提供对住院病人的电子病历书写、保存......
  • springboot2.4下使用JUnit依赖注入失败的解决方案
    首先在pom.xml下引入JUnit必须的包:<dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId&......