2021年你还不会Shiro?----6.Shiro与SpringBoot的集成

简介: 当下最流行的java框架就是SpringCloud与SpringBoot了,这篇文章总结了下Shiro与SpringBoot的集成使用,做了一个简单的登录功能。但是登录功能依然未使用数据库,数据是模拟的,下篇文章里会总结Shiro+JSP+SpringBoot+MyBatis+mysql来实现认证授权的真实场景。

前言:


当下最流行的java框架就是SpringCloud与SpringBoot了,这篇文章总结了下Shiro与SpringBoot的集成使用,做了一个简单的登录功能。但是登录功能依然未使用数据库,数据是模拟的,下篇文章里会总结Shiro+JSP+SpringBoot+MyBatis+mysql来实现认证授权的真实场景。


一.整合过程



首先需要明确使用Shiro的目的,就是为了让系统中的部分资源不可以被随便访问,或者是接口,或者是静态资源,这类资源的访问我们要对请求进行拦截,怎么拦截呢,Shiro中提供了ShiroFilter用来拦截这些访问受限资源的请求,我们只需要将ShiroFilter注入到Spring的容器中即可,然后配置下ShiroFilter需要过滤哪些请求就可以正常使用了。既然很多请求需要被Shiro过滤后才能访问,那么也有部分资源时不需要被拦截的,比如我们的登录页,注册页,亦或者商城的商品浏览页,商品详情页等等这些都是不能拦截的,这部分就是公共资源了。对于公共资源我们应该进行放行。下面我们就来看下怎么一步步实现Shiro与SpringBoot的整合吧。


image.png


1.使用Spring initializr创建SpringBoot工程


如下图所示,我们FIle–>New–>Project下选择该功能,然后可以快速创建一个SpringBoot的工程,若是默认的仓库中没有所需jar,就会使用图片中选中的地址进行下载模板。


20210324093354979.png


2.选择jdk版本,选择启动器


创建项目时,注意这两块的选择就行,项目名随便起,看心情吧,JDK选择自己开发环境已经安装了的就行,不过使用Sping initializr功能,JDK7可能已经不支持了,最好是JDK8或者JDK11,这俩都是目前仍在更新的JDK的大版本,其余的JDK16以下的版本都已经停止更新了。


20210324094032426.png


然后就是选择我们需要的启动器了,启动器这里只需要选择支持JSP的启动器,顺便选择lombok,这是支持类实体注解的依赖,此外还需要选择web开发的启动器就行,如下图。20210324094656786.png


3.创建jsp页面启动工程


到这里其实一个web工程我们就创建完成了,如果网络不好,我们需要静静等待一会,让项目将jar包下载完成。依赖下载完成后,我们需要创建几个jsp页面用来与后台接口进行交互。我们在main的下面创建一个webapp文件夹用来存放jsp页面文件。


20210324095200870.png20210324095258390.png


我们新建一个login.jsp的页面,页面内容如下,这也是jsp的标准模板

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>标题</title>
    <style type="text/css">
        *{margin: 0;padding: 0;}
        form{margin: 0 auto;padding:15px; width: 300px;height:300px;text-align: center;}
        #submit{padding: 10px}
        #submit input{width: 50px;height: 24px;}
    </style>
</head>
<body>
    <h1>登录页</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用户名:<input type="text" name ="username"/><br/>
        密 码 :<input type="text" name ="password"/><br/>
        <input type="submit" value="登录"><br/>
    </form>
</body>
</html>


再创建一个index.jsp页面,供登录成功后进行展示。如下图:

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>标题</title>
    <style type="text/css">
        *{margin: 0;padding: 0;}
        form{margin: 0 auto;padding:15px; width: 300px;height:300px;text-align: center;}
        #submit{padding: 10px}
        #submit input{width: 50px;height: 24px;}
    </style>
</head>
<body>
    <h1>系统主页</h1>
    <ul>
        <a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
        <li><a href="">用户管理</a></li>
        <li><a href=""></a>商品管理</li>
        <li><a href=""></a>商户管理</li>
        <li><a href=""></a>内容管理</li>
    </ul>
</body>
</html>


jsp页面创建完成后,我们来配置下application.yml配置文件,配置下服务端口、服务名、配置前端文件支持jsp等,如下所示:

# 服务端口
server.port=8888
# 项目访问路径
server.servlet.context-path=/shiro
# 项目名
spring.application.name=shiro
# 配置mvc的视图解析器支持jsp,默认不支持
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp


4.启动工程查看登录页面,登录成功配置依赖


20210324100832595.png


从图中看出,我们的项目已经正常启动了,如果没有正常启动,可以编辑下该工程的配置信息更改下Working directory.将该值改成如下图所示,然后重启就会正常了。

20210324101046600.png


这是我们项目已经启动正常,然后加入Shiro与JSP的依赖。如下所示:

<!--引入支持jsp的依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--引入支持jstl的依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!--引入支持shiro的启动器-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>


5.创建controller,自定义Realm,并将Realm等其他对象注入到Spring容器中


我们需要提供一个简单的控制器,里面有登录和退出登录的方法。供jsp的调用。代码如下

@Controller
@RequestMapping("/user")
public class LoginController {
    @PostMapping("/login")
    public String login(String username,String password){
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
            return "redirect:/login.jsp";
        } catch(IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
            return "redirect:/login.jsp";
        }
        return "redirect:/index.jsp";
    }
    @RequestMapping("logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }
}

控制器写好了,我们还需要写一个自己的Realm,我们都知道,Shiro的数据来源都是Realm,所以我们实现自己的登录必须自己实现一个Realm用来获取认证和授权的信息,这里先不实现授权的部分,先只是进行认证的流程操作,不过在认证时依然使用MD5+盐+hash散列的方式对密码进行加密(前面的文章已经说过怎么实现这里就直接写了)。


public class FirstRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("进入认证方法");
        String username = (String)authenticationToken.getPrincipal();
        Md5Hash md5Hash = new Md5Hash("123","234@#$",1024);
        if(username.equals("luban")){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("luban",md5Hash.toString(), ByteSource.Util.bytes("234@#$"),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}


Realm定义完成以后,我们需要将自定义的Realm注入到Spring容器中,将对象注入到容器中我们可以在xml文件中实现,也可以使用注解实现,最常用的还是通过配置类来实现,即我们自己定义一个配置类通过@Bean注解将对象注入到Spring容器中,如下所示:


@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String,String> map = new HashMap<>();
        map.put("/user/login","anon");//表示该资源无需认证授权,无需授权的应该写在上面
        map.put("/user/logout","anon");//表示该资源无需认证授权
        map.put("/login.jsp","anon");//表示该资源无需认证授权
        map.put("/**","authc");//表示所有资源都需要经过认证授权
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        //设置授权失败返回的页面
        shiroFilterFactoryBean.setLoginUrl("login.jsp");//这也是默认值
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(FirstRealm firstReaml){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(firstReaml);
        return defaultWebSecurityManager;
    }
    @Bean
    public FirstRealm getRealm(){
        FirstRealm firstRealm = new FirstRealm();
        Md5CredentialsMatcher md5CredentialsMatcher = new Md5CredentialsMatcher();
        md5CredentialsMatcher.setHashIterations(1024);
        firstRealm.setCredentialsMatcher(md5CredentialsMatcher);
        return firstRealm;
    }
}


如上所示,ShiroFilter是通过ShiroFilterFactoryBean来获取,这个ShiroFilter的工厂,然后为他注入SecurityManager,这里的SecurityManager是DefaultWebSecurityManager,这个是适用于web版本的安全管理器,web开发就使用它就对了,此外在FirstRealm这个自定义的Realm中我们需要自定义一个密码匹配器,用来匹配MD5+盐+hash散列来加密的密码,并告诉该匹配器我们的密码经过了多少次散列,然后就可以了。


下面列出了,ShiroFilter中支持的配置参数,以及对应的含义,通常都是使用最上面的两种。

20210322112813317.png


6.启动工程,测试登录功能。


到这里我们的采用MD5+盐+hash散列的Shiro+SpringBoot实现的登录功能就完成了,接下来我们来测试下,我们使用的是账号是luban:123,我们先使用luban:1234来登录测试下


20210324113241752.png


然后发现登录失败又跳回了登录页,这是因为我们在配置ShiroFilter时,配置了认证失败的返回页就是login.jsp,所以又跳回了这里。

后端报错如下,很明显这是密码错误。


image.png


那我们在使用账号luban:123登录测试下,结果如下:


20210324134416708.png


我们可以发现已经正常进入到系统内部了,这样就验证了我们使用Shiro+SpringBoot实现的登录功能就完全正常了。


二.整合中的问题与思考



在整合中我们可能会碰到各种问题,这里整理了下,我在整合过程中碰到的一些问题以及思考。


1.ShiroFilter过滤路径配置问题


在配置我们需要过滤的路径与需要排除的路径时,有的人说需要将公共资源的路径配置在上面,受限资源配置在下面,就像这种

Map<String,String> map = new HashMap<>();
map.put("/user/login","anon");//表示该资源无需认证授权,无需授权的应该写在上面
map.put("/user/logout","anon");//表示该资源无需认证授权
map.put("/login.jsp","anon");//表示该资源无需认证授权
map.put("/**","authc");//表示所有资源都需要经过认证授权
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);


但是经测试将过滤资源的代码,放在公共资源的上方是可以正常使用的,当然不排除版本变更修复了这个问题,但是从目前测试使用的Shiro1.5.3以后配置公共资源与受限资源肯定不需要考虑顺序了。


2.配置了login.jsp为受限资源未登录的展示页,还需要配login.jsp为公共资源吗


有人说配置了受限资源访问不成功就重定向到登录页,然后就不需要为登录页设置公共资源的标识了,在ShiroFilter中我们可以看到,当前我写的demo是配置了登录页为公共资源的,在我测试该场景时,如果仅仅指定受限资源未登录的展示页,然后我们访问受限资源index.jsp就会报这个问题,如下所示:


20210324135828275.png


这个错误图中已经说了,就是因为系统重定向次数过多所导致,访问index.jsp我们没有登录系统就将我们重定向到login.jsp然后,发现login.jsp也不是公共资源继续重定向login.jsp就陷入死循环了,所以这里我们得配置login.jsp为公共资源。然后就正常了。如下:


map.put("/login.jsp","anon");//表示该资源无需认证授权


3.登录成功后非正常情况退出,重新登录系统,用户名和密码错误也会进入系统


当然这里展示的代码并没有这个问题,在我发现这个问题后已经修改了代码,主要是这块代码

 @PostMapping("/login")
  public String login(String username,String password){
      UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
      Subject subject = SecurityUtils.getSubject();
      try {
          subject.login(usernamePasswordToken);
      } catch (UnknownAccountException e) {
          e.printStackTrace();
          System.out.println("用户名错误");
          return "redirect:/login.jsp";
      } catch(IncorrectCredentialsException e){
          e.printStackTrace();
          System.out.println("密码错误");
          return "redirect:/login.jsp";
      }
      return "redirect:/index.jsp";
  }


说下这个错误的原因,等用户登录的用户名或者密码错误时,都会被程序catch住,然后抛出异常,程序继续执行下去就会返回inde.jsp页面,又因为刚刚没有正常退出,第一次登录后,Shiro会缓存登录信息,所以我们可以正常进入到index.jsp页面,我们只需要在catch住错误时,直接返回到login.jsp就可以正常了。


三.总结



这篇文章里总结了Shiro与SpringBoot的整合,思路也很明确,先引入Shiro的依赖,然后自定义Realm,然后通过配置类注入Realm与Filter、SecurityManager等的对象,再提高登录的接口,登录的页面,与登录的首页等,最后分析了可能会碰到的几个问题,希望对路过的朋友有所帮助。


相关文章
|
2月前
|
安全 Java 数据库
第16课:Spring Boot中集成 Shiro
第16课:Spring Boot中集成 Shiro
419 0
|
6月前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
282 0
|
2月前
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
328 0
第07课:Spring Boot集成Thymeleaf模板引擎
|
3月前
|
前端开发
SpringBoot2.3.1集成Knife4j接口文档
SpringBoot2.3.1集成Knife4j接口文档
410 44
|
2月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
282 2
|
2月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
115 2
|
2月前
|
存储 人工智能 Java
Springboot集成AI Springboot3 集成阿里云百炼大模型CosyVoice2 实现Ai克隆语音(未持久化存储)
本项目基于Spring Boot 3.5.3与Java 17,集成阿里云百炼大模型CosyVoice2实现音色克隆与语音合成。内容涵盖项目搭建、音色创建、音频合成、音色管理等功能,适用于希望快速掌握Spring Boot集成语音AI技术的开发者。需提前注册阿里云并获取API Key。
|
3月前
|
缓存 安全 Java
Shiro简介及SpringBoot集成Shiro(狂神说视频简易版)
Shiro简介及SpringBoot集成Shiro(狂神说视频简易版)
229 6
|
4月前
|
缓存 Java 数据库
SpringBoot集成Ehcache缓存使用指南
以上是SpringBoot集成Ehcache缓存的基本操作指南,帮助你在实际项目中轻松实现缓存功能。当然,Ehcache还有诸多高级特性,通过学习和实践,你可以更好地发挥它的威力。
404 20
|
5月前
|
Java 开发工具 Spring
【Azure Application Insights】为Spring Boot应用集成Application Insight SDK
本文以Java Spring Boot项目为例,详细说明如何集成Azure Application Insights SDK以收集和展示日志。内容包括三步配置:1) 在`pom.xml`中添加依赖项`applicationinsights-runtime-attach`和`applicationinsights-core`;2) 在main函数中调用`ApplicationInsights.attach()`;3) 配置`applicationinsights.json`文件。同时提供问题排查建议及自定义日志方法示例,帮助用户顺利集成并使用Application Insights服务。
111 8

热门文章

最新文章