SpringBoot整合Shiro详解,还在自己写登陆注册早落伍了

点赞再看,养成习惯,听说微信搜公众号《Java鱼仔》会让自己的技术更上一层楼

(一)概述

Shiro是Apache的一个安全框架,Shiro可以非常容易的开发出安全性足够好的应用,Shiro可以完成认证、授权、加密、会话管理、缓存等功能。
从应用程序的角度来观察Shiro,我们可以发现Shiro的运行过程主要如下:
在这里插入图片描述
应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:

Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

(二)SpringBoot整合Shiro

首先引入Shiro依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

通过上面的这些概念,我们开始来写代码,使用Shiro只需要三步

1.创建realm对象,需要自定义

2.创建安全管理器,绑定realm对象

3.创建Shiro过滤工厂,绑定安全管理器

首先第一步,自定义一个realm对象

public class UserRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}

定义一个UserRealm继承AuthorizingRealm 并实现接口方法,两个方法分别代表授权和认证,这里的实现方式和SpringSecurity十分类似。

接下来创建一个ShiroConfig类,来实现shiro的配置

@Configuration
public class ShiroConfig {
    //创建realm对象,需要自定义
    @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
    //创建安全管理器
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //绑定realm对象
        securityManager.setRealm(userRealm());
        return securityManager;
    }
    //创建Shiro过滤工厂
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }
}

(三) shiro实现登陆拦截

在Shiro实现登陆拦截只需要在shiro的过滤工厂配置过滤器即可,为了更加具体的展示效果,我们需要新建四个html页面:index、level1、level2、login

另外需要引入thymeleaf依赖以及spring-boot-starter-web依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

index.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!--引入thymeleaf-->
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<h2 th:text="${msg}"></h2>
<a th:href="@{/level1}">level1</a>
<a th:href="@{/level2}">level2</a>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登陆页</title>
</head>
<body>
<div>
    <p th:text="${errormsg}"></p>
    <form action="/checklogin" method="post">
        <h2>登陆页</h2>
        <input type="text" id="username"  name="username" placeholder="username">
        <input type="password" id="password" name="password"  placeholder="password">
        <button type="submit">登陆</button>
    </form>
</div>
</body>
</html>

level1、level2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
level1
</body>
</html>

然后编写indexController

@Controller
public class indexController {
    @RequestMapping({"/","/index"})
    public String index(Model model){
        model.addAttribute("msg","hello,shiro");
        return "index";
    }
    @RequestMapping("/level1")
    public String level1(Model model){
        return "level1";
    }
    @RequestMapping("/level2")
    public String level2(Model model){
        return "level2";
    }
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String tologin(){
        return "login";
    }
}

在这些前期工作完成后,我们就可以配置过滤拦截器了,在ShiroConfig类中的ShiroFilterFactoryBean 方法中,添加所需要配置的过滤器,shiro内置了五种过滤器

/**
 * anon : 无需认证就可以访问
 * authc: 必须认证了才能访问
 * user: 必须有 记住我 功能才能访问
 * perms: 拥有对某个资源的权限才能访问
 * role: 拥有某个角色权限才能访问
 */

在使用时,只要将对应的页面分配不同的过滤器即可

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
    ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
    //设置安全管理器
    bean.setSecurityManager(defaultWebSecurityManager);
    //添加shiro的内置过滤器
    Map<String,String> filterMap=new LinkedHashMap<>();
    filterMap.put("/index","anon");
    filterMap.put("/level1","authc");
    filterMap.put("/level2","authc");
    bean.setFilterChainDefinitionMap(filterMap);
    //设置跳转登陆页
    bean.setLoginUrl("/login");
    return bean;
}

在这段代码中,我们设置index首页无需权限就可以访问,level1何level2需要认证了才能访问,登陆页跳转到login页面。实现的效果为进入index首页无需任何认证,当点击level1和level2标签时判断有无认证,如果没有认证自动跳转到login页面。

(四)Shiro实现用户认证

shiro的用户认证都放在realm中进行,首先我们需要改造一下controller中对登陆逻辑的处理,我们需要将用户传过来的用户名和密码封装到shiro中,新写一个方法判断登陆情况

@RequestMapping(value = "/checklogin",method = RequestMethod.POST)
public String login(@RequestParam("username") String username,@RequestParam("password") String password,Model model){
    //获取当前的用户
    Subject subject = SecurityUtils.getSubject();
    //用来存放错误信息
    String msg="";
    //如果未认证
    if (!subject.isAuthenticated()){
         //将用户名和密码封装到shiro中
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            // 执行登陆方法
            subject.login(token);
        }catch (UnknownAccountException e){//用户名不存在
            msg=e.getMessage();
        }catch (IncorrectCredentialsException e){//密码错误
            msg=e.getMessage();
        }catch (Exception e){
            msg="用户登陆异常";
            e.printStackTrace();
        }
        //如果msg为空,说明没有异常,就返回到主页
        if (msg.isEmpty()){
            return "redirect:/index";
        }else {
            model.addAttribute("errormsg",msg);
            return "login";
        }
    }
    return "login";
}

在这个登陆控制器中,我们首先通过subject判断用户是否存在,如果不存在就封装用户数据进行login。这个login的动作就需要在realm中去执行。回到UserRealm的认证方法:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    //获取到token
    UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
    //从token中获取到用户名和密码
    String username = token.getUsername();
    String password = String.valueOf(token.getPassword());
    //为了方便,这里不从数据库中获取用户
    if (!"root".equals(username)) {
        throw new UnknownAccountException("用户不存在");
    }else if (!"123456".equals(password)){
        throw new IncorrectCredentialsException("密码错误");
    }
    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"123456",getName());
    return info;
}

在之前的代码中我们将用户名和密码存入了UsernamePasswordToken里,在UserRealm中可以通过authenticationToken获取到,获取到用户名和密码之后,我们可以将用户名和密码与数据库进行对比,并向上抛出不同的异常,这些异常会被e.getMessage()所获取到。

这里需要注意的是这个返回值SimpleAuthenticationInfo ,这个类是AuthenticationInfo 的实现类,SimpleAuthenticationInfo的构造方法需要传入三个参数:

第一个参数是principal,一般会传入用户名或者用户实体类,然后在其他地方通过下面这段代码获取到当前登陆的用户

SecurityUtils.getSubject().getPrincipal();

第二个参数是密码,注意这里是指数据库中的密码,因为我们在前面的代码中已经做了一层密码判断,这里的密码校验就没有多大效果。

第三个参数是Realm的名称,直接使用getName()方法获取即可。

(4.1)效果展示

进入首页后点击level1或者level2自动跳转登陆页
在这里插入图片描述
进入登陆页后如果用户名输入错误显示用户不存在,密码错误则展示密码错误
在这里插入图片描述
当用户名和密码都输入正确后进入首页,此时点击level1或者level2都进入两个页面。

(五)Shiro实现用户授权

在前面我们实现了用户的认证,接下来我们来做用户授权,用户授权在很多场景下都可以见到,比如一个网站的某些页面只有vip可以见到。

我们来模拟上面的这个场景,首先修改ShiroConfiggetShiroFilterFactoryBean方法,增加权限

 @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //添加shiro的内置过滤器
        Map<String,String> filterMap=new LinkedHashMap<>();
        //level1只有vip1才能访问
        filterMap.put("/level1","perms[vip1]");
        filterMap.put("/index","anon");
        bean.setFilterChainDefinitionMap(filterMap);
        bean.setLoginUrl("/login");
        //如果没有权限跳转的页面
        bean.setUnauthorizedUrl("/unauthorizedUrl");
        return bean;
    }

level1只有vip1才能访问,如果没有权限就会跳转到/unauthorizedUrl,我们先把这个跳转后的路径写好:

@RequestMapping("/unauthorizedUrl")
@ResponseBody
public String unAuthorizedUrl(){
    return "当前用户没有权限访问";
}

接着在UserRealm中编写授权部分的代码:

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //在这里为每一个用户添加vip1权限
    info.addStringPermission("vip1");
    return info;
}

在这段代码中通过addStringPermission为所有用户增加了vip1权限,在真实的环境中,我们会根据不同的用户给出不同的权限,比如在数据库中增加一个权限字段,上面代码中填写vip1的地方用数据库中的权限字段代替。

(六)总结

至此,我们已经将shiro的引入、基本使用、认证、授权都学完一遍了。Shiro和SpringSecurity各有优点,具体选择用哪个框架,看你们公司的选择吧。

适用人群 Java开发人员,Vue开发人员,前后端分离开发人员,权限管理和配置开发人员 课程概述 【讲师介绍】 讲师职称:               现某知名大型互联网公司资深架构师,技术总监,职业规划师,首席面试官,曾在某上市培训机构,高校任教多年。             Array(Array老师)10多年互联网公司实战经验,知名的大型互联网公司的架构师,高管等职,在企业长期从事于技术的源码阅读和新技术的研究;擅长于职业规划,面试辅导,从事面试官多年;擅长于JAVA,人工智能AI应用,Xmind等等,曾服役于国内某上市培训机构数年,独特的培训思路,培训体系,培训方式,实践的职场技能,职场现状,职场晋升等让你快速适应企业职场的所需。  【课程介绍】 技术选型 开发环境:Eclipse/Idea ,JDK 1.8以上  后端技术 核心框架:SpringBoot2.x框架系列(同样适用Springcloud F版本以后的版本),如下(节选):     持久层框架:MyBatis 3.x + Mybatis-plus 3.x 日志管理:SLF4J 1.7 + Log4j2 2.7 工具类:Apache Commons、Jackson 、fastjson、Gson 权限验证 前端技术   Vue   Vue-cli ElementUI ---https://element.eleme.io/ JSX (JavaScript Xml) 前台的权限验证和路由设置 开发模式        前后端分离的开发 数据库        Mysql5 IDE     Intellij Idea
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页