springboot结合shiro实战-身份认证

 

目录

环境搭建

配置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 {@Beanpublic 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*/@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition() {DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();//登录不需要校验权限chainDefinition.addPathDefinition("/login", "anon");//其他任何url都需要校验是否登录chainDefinition.addPathDefinition("/**", "authc");return chainDefinition;}@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("mySecurityManager") SecurityManager securityManager) {ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();filterFactoryBean.setSecurityManager(securityManager);filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());return filterFactoryBean;}/*** 开启Shiro注解模式,可以在Controller中的方法上添加注解* @param securityManager* @return*/@Beanpublic 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 {@Resourceprivate UserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 实现授权逻辑SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//获得当前subjectSubject subject = SecurityUtils.getSubject();//获得当前的principal,也就是认证完后我们放入的信息User currentUser = (User) subject.getPrincipal();//添加权限info.addStringPermissions(currentUser.getPerms());return info;}@Overrideprotected 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中密码验证不需要我们自己去做;// 第三个参数为 realmNamereturn 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();//获得当前subjectSubject 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中密码验证不需要我们自己去做;// 第三个参数为 realmNamereturn 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.hqwc.cn/news/70428.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

键盘居然“软硬可调”,达尔优的GAS可调系统究竟是什么?

达尔优在7月29日上海 ChinaJoy 2023上发布了A98系列最新的重磅产品——A98Master机械键盘。采用了行业首创的GAS可调结构,被粉丝成为“绝技”,GAS可调系统到底有什么作用?还特地做这么隆重的发布? 达尔优的GAS可调系统简单说是Gasket方案软硬可调结构。通过小工具调节键盘的内…

linux 学习————LNMP之分布式部署

目录 一、概述 二、LNMP环境部署 三、配置nginx 四、 配置php使nginx能够解析.php 五、配置mysql 六、配置discuz进行登录论坛访问测试 一、概述 LNMP代表 Linux、Nginx、MySQL、PHP&#xff0c;是一种常用的服务器架构。它由以下组件组成&#xff1a; Linux&#xff1a;作…

Layui列表复选框根据条件禁用

// 禁用客服回访id有值的复选框res.data.forEach(function (item, i) {if (item.feedbackEmpId) {let index res.data[i][LAY_TABLE_INDEX];$(".layui-table tr[data-index"index"] input[typecheckbox]").prop(disabled,true);$(".layui-table tr[d…

大学教授开设ChatGPT课程,深入了解如何利用ChatGPT增强学生学习力

作者&#xff1a;ANDREW MAYNARD&#xff0c;亚利桑那州立大学教授 六月底&#xff0c;我开始教授一门由ChatGPT设计的关于使用ChatGPT&#xff0c;并由ChatGPT评估的课程。 这个想法源于看到“提示工程师”工作的新闻&#xff0c;这类工作据说每年可以赚到30多万美元。作为一…

C++继承

文章目录 继承的语法继承后成员访问方式变化基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元菱形继承组合 继承的语法 class Person { protected:char name[20];int age;char address[20]; };class Student : public Person { protected:int _stuid;//…

【mysql报错解决】MySql.Data.MySqlClient.MySqlException (0x80004005)或1366

场景&#xff1a;c#使用mysql数据库执行数据库迁移&#xff0c;使用了新增inserter的语句&#xff0c;然后报错 报错如下&#xff1a; 1.MySql.Data.MySqlClient.MySqlException (0x80004005): Incorrect string value: ‘\xE6\x9B\xB4\xE6\x94\xB9…’ for column ‘Migratio…

分布式文件存储系统-FastDFS

前言&#xff1a;FastDFS 是一个分布式文件存储系统&#xff0c;主要用于存储和管理大规模的文件数据&#xff0c;如图片、视频、文档等&#xff0c;是淘宝前架构师为了存储图片用C语言开发出来的系统。 服务端有两个组件 Tracker Server 与 Storage Server &#xff0c;对应两…

JMeter 特殊组件-逻辑控制器与BeanShell PreProcessor 使用示例

文章目录 前言JMeter 特殊组件-逻辑控制器与BeanShell PreProcessor 使用示例1. 逻辑控制器使用1.1. While Controller 使用示例1.2. 如果&#xff08;If&#xff09;控制器 使用示例 2. BeanShell PreProcessor 使用示例 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞…

最小生成树 — Prim算法

同Kruskal算法一样&#xff0c;Prim算法也是最小生成树的算法&#xff0c;但与Kruskal算法有较大的差别。 Prim算法整体是通过“解锁” “选中”的方式&#xff0c;点 -> 边 -> 点 -> 边。 因为是最小生成树&#xff0c;所以针对的也是无向图&#xff0c;所以可以随意…

ES6自用笔记

原型链 引用类型&#xff1a;__proto__(隐式原型)属性&#xff0c;属性值是对象函数&#xff1a;prototype(原型)属性&#xff0c;属性值是对象 ​​​​​​​ 相关方法 person.prototype.isPrototypeOf(stu) Object.getPrototypeOf(Object)替换已不推荐的Object._ _ pro…

系统架构设计专业技能 · 软件工程之软件测试与维护(六)【系统架构设计师】

系列文章目录 系统架构设计专业技能 网络规划与设计&#xff08;三&#xff09;【系统架构设计师】 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师…

HDFS原理剖析

一、概述 HDFS是Hadoop的分布式文件系统&#xff08;Hadoop Distributed File System&#xff09;&#xff0c;实现大规模数据可靠的分布式读写。HDFS针对的使用场景是数据读写具有“一次写&#xff0c;多次读”的特征&#xff0c;而数据“写”操作是顺序写&#xff0c;也就是…