权限认证.md 62 KB

[TOC]

1、SpringBoot:集成SpringSecurity

1.1、安全简介

在 Web 开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。因此,从应用开发的第一天就应该把安全相关的因素考虑进来,并在整个应用的开发过程中。

市面上存在比较有名的:Shiro,Spring Security !

这里需要阐述一下的是,每一个框架的出现都是为了解决某一问题而产生了,那么Spring Security框架的出现是为了解决什么问题呢?

首先我们看下它的官网介绍:Spring Security官网地址

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。

Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求

从官网的介绍中可以知道这是一个权限框架。想我们之前做项目是没有使用框架是怎么控制权限的?对于权限 一般会细分为功能权限,访问权限,和菜单权限。代码会写的非常的繁琐,冗余。

怎么解决之前写权限代码繁琐,冗余的问题,一些主流框架就应运而生而Spring Scecurity就是其中的一种。

Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

1.2、实战测试

1.2.1、实验环境搭建

1、新建一个初始的springboot项目web模块,thymeleaf模块

2、导入静态资源

welcome.html
|views
|level1
1.html
2.html
3.html
|level2
1.html
2.html
3.html
|level3
1.html
2.html
3.html
Login.html

3、controller跳转!

package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {

   @RequestMapping({"/","/index"})
   public String index(){
       return "index";
  }

   @RequestMapping("/toLogin")
   public String toLogin(){
       return "views/login";
  }

   @RequestMapping("/level1/{id}")
   public String level1(@PathVariable("id") int id){
       return "views/level1/"+id;
  }

   @RequestMapping("/level2/{id}")
   public String level2(@PathVariable("id") int id){
       return "views/level2/"+id;
  }

   @RequestMapping("/level3/{id}")
   public String level3(@PathVariable("id") int id){
       return "views/level3/"+id;
  }

}

4、测试实验环境是否OK!

1.2.2、认识SpringSecurity

Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!

记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。

“认证”(Authentication)

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。

身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

“授权” (Authorization)

授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。

这个概念是通用的,而不是只在Spring Security 中存在。

1.2.3、认证和授权

目前,我们的测试环境,是谁都可以访问的,我们使用 Spring Security 增加上认证和授权的功能

1、引入 Spring Security 模块

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、编写 Spring Security 配置类

参考官网:https://spring.io/projects/spring-security

查看我们自己项目中的版本,找到对应的帮助文档:

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

img

3、编写基础配置类

package com.kuang.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       
  }
}

4、定制请求的授权规则

@Override
protected void configure(HttpSecurity http) throws Exception {
   // 定制请求的授权规则
   // 首页所有人可以访问
   http.authorizeRequests().antMatchers("/").permitAll()
  .antMatchers("/level1/**").hasRole("vip1")
  .antMatchers("/level2/**").hasRole("vip2")
  .antMatchers("/level3/**").hasRole("vip3");
}

5、测试一下:发现除了首页都进不去了!因为我们目前没有登录的角色,因为请求需要登录的角色拥有对应的权限才可以!

6、在configure()方法中加入以下配置,开启自动配置的登录功能!

// 开启自动配置的登录功能
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin();

7、测试一下:发现,没有权限的时候,会跳转到登录的页面!

img

8、查看刚才登录页的注释信息;

我们可以定义认证规则,重写configure(AuthenticationManagerBuilder auth)方法

//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
   //在内存中定义,也可以在jdbc中去拿....
   auth.inMemoryAuthentication()
          .withUser("kuangshen").password("123456").roles("vip2","vip3")
          .and()
          .withUser("root").password("123456").roles("vip1","vip2","vip3")
          .and()
          .withUser("guest").password("123456").roles("vip1","vip2");
}

9、测试,我们可以使用这些账号登录进行测试!发现会报错!

There is no PasswordEncoder mapped for the id “null”

img

10、原因,我们要将前端传过来的密码进行某种方式加密,否则就无法登录,修改代码

//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   //在内存中定义,也可以在jdbc中去拿....
   //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
   //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
   //spring security 官方推荐的是使用bcrypt加密方式。
   
   auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
          .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
          .and()
          .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
          .and()
          .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
}

11、测试,发现,登录成功,并且每个角色只能访问自己认证下的规则!搞定

1.2.4、权限控制和注销

1、开启自动配置的注销的功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
   //....
   //开启自动配置的注销的功能
      // /logout 注销请求
   http.logout();
}

2、我们在前端,增加一个注销的按钮,index.html 导航栏中

<a class="item" th:href="@{/logout}">
   <i class="address card icon"></i> 注销
</a>

3、我们可以去测试一下,登录成功后点击注销,发现注销完毕会跳转到登录页面!

4、但是,我们想让他注销成功后,依旧可以跳转到首页,该怎么处理呢?

// .logoutSuccessUrl("/"); 注销成功来到首页
http.logout().logoutSuccessUrl("/");

5、测试,注销完毕后,发现跳转到首页OK

6、我们现在又来一个需求:用户没有登录的时候,导航栏上只显示登录按钮,用户登录之后,导航栏可以显示登录的用户信息及注销按钮!还有就是,比如kuangshen这个用户,它只有 vip2,vip3功能,那么登录则只显示这两个功能,而vip1的功能菜单不显示!这个就是真实的网站情况了!该如何做呢?

我们需要结合thymeleaf中的一些功能

sec:authorize="isAuthenticated()":是否认证登录!来显示不同的页面

Maven依赖:

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
   <groupId>org.thymeleaf.extras</groupId>
   <artifactId>thymeleaf-extras-springsecurity5</artifactId>
   <version>3.0.4.RELEASE</version>
</dependency>

7、修改我们的 前端页面

  1. 导入命名空间

  2. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
    
    1. 修改导航栏,增加认证判断

    2. ```html

    3.   <a class="item" th:href="@{/login}">
            <i class="address card icon"></i> 登录
        </a>
      

        <a class="item">
            <i class="address card icon"></i>
           用户名:<span sec:authentication="principal.username"></span>
           角色:<span sec:authentication="principal.authorities"></span>
        </a>
      

        <a class="item" th:href="@{/logout}">
            <i class="address card icon"></i> 注销
        </a>
      

      ```

8、重启测试,我们可以登录试试看,登录成功后确实,显示了我们想要的页面;

9、如果注销404了,就是因为它默认防止csrf跨站请求伪造,因为会产生安全问题,我们可以将请求改为post表单提交,或者在spring security中关闭csrf功能;我们试试:在 配置中增加 http.csrf().disable();

http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.logout().logoutSuccessUrl("/");

10、我们继续将下面的角色功能块认证完成!

<!-- sec:authorize="hasRole('vip1')" -->
<div class="column" sec:authorize="hasRole('vip1')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 1</h5>
               <hr>
               <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
               <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
               <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
           </div>
       </div>
   </div>
</div>

<div class="column" sec:authorize="hasRole('vip2')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 2</h5>
               <hr>
               <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
               <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
               <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
           </div>
       </div>
   </div>
</div>

<div class="column" sec:authorize="hasRole('vip3')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 3</h5>
               <hr>
               <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
               <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
               <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
           </div>
       </div>
   </div>
</div>

11、测试一下!

12、权限控制和注销搞定!

1.2.5、记住我

现在的情况,我们只要登录之后,关闭浏览器,再登录,就会让我们重新登录,但是很多网站的情况,就是有一个记住密码的功能,这个该如何实现呢?很简单

1、开启记住我功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//。。。。。。。。。。。
   //记住我
   http.rememberMe();
}

2、我们再次启动项目测试一下,发现登录页多了一个记住我功能,我们登录之后关闭 浏览器,然后重新打开浏览器访问,发现用户依旧存在!

思考:如何实现的呢?其实非常简单

我们可以查看浏览器的cookie

img

3、我们点击注销的时候,可以发现,spring security 帮我们自动删除了这个 cookie

img 4、结论:登录成功后,将cookie发送给浏览器保存,以后登录带上这个cookie,只要通过检查就可以免登录了。如果点击注销,则会删除这个cookie,具体的原理我们在JavaWeb阶段都讲过了,这里就不在多说了!

1.2.6、定制登录页

现在这个登录页面都是spring security 默认的,怎么样可以使用我们自己写的Login界面呢?

1、在刚才的登录页配置后面指定 loginpage

http.formLogin().loginPage("/toLogin");

2、然后前端也需要指向我们自己定义的 login请求

<a class="item" th:href="@{/toLogin}">
   <i class="address card icon"></i> 登录
</a>

3、我们登录,需要将这些信息发送到哪里,我们也需要配置,login.html 配置提交请求及方式,方式必须为post:

在 loginPage()源码中的注释上有写明:

img

<form th:action="@{/login}" method="post">
   <div class="field">
       <label>Username</label>
       <div class="ui left icon input">
           <input type="text" placeholder="Username" name="username">
           <i class="user icon"></i>
       </div>
   </div>
   <div class="field">
       <label>Password</label>
       <div class="ui left icon input">
           <input type="password" name="password">
           <i class="lock icon"></i>
       </div>
   </div>
   <input type="submit" class="ui blue submit button"/>
</form>

4、这个请求提交上来,我们还需要验证处理,怎么做呢?我们可以查看formLogin()方法的源码!我们配置接收登录的用户名和密码的参数!

http.formLogin()
  .usernameParameter("username")
  .passwordParameter("password")
  .loginPage("/toLogin")
  .loginProcessingUrl("/login"); // 登陆表单提交请求

5、在登录页增加记住我的多选框

<input type="checkbox" name="remember"> 记住我

6、后端验证处理!

//定制记住我的参数!
http.rememberMe().rememberMeParameter("remember");

7、测试,OK

1.3、完整配置代码

package com.kuang.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   //定制请求的授权规则
   @Override
   protected void configure(HttpSecurity http) throws Exception {

       http.authorizeRequests().antMatchers("/").permitAll()
      .antMatchers("/level1/**").hasRole("vip1")
      .antMatchers("/level2/**").hasRole("vip2")
      .antMatchers("/level3/**").hasRole("vip3");


       //开启自动配置的登录功能:如果没有权限,就会跳转到登录页面!
           // /login 请求来到登录页
           // /login?error 重定向到这里表示登录失败
       http.formLogin()
          .usernameParameter("username")
          .passwordParameter("password")
          .loginPage("/toLogin")
          .loginProcessingUrl("/login"); // 登陆表单提交请求

       //开启自动配置的注销的功能
           // /logout 注销请求
           // .logoutSuccessUrl("/"); 注销成功来到首页

       http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
       http.logout().logoutSuccessUrl("/");

       //记住我
       http.rememberMe().rememberMeParameter("remember");
  }

   //定义认证规则
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       //在内存中定义,也可以在jdbc中去拿....
       //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
       //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
       //spring security 官方推荐的是使用bcrypt加密方式。

       auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
              .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
              .and()
              .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
              .and()
              .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
  }
}

去进行一波源码分析!**

2、Shiro简介

2.1、Shiro 是什么?

  • Apache Shiro 是 Java 的一个安全(权限)框架。

  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

  • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

  • 下载地址

2.2、有哪些功能?

image-20200729114647110

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份

  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限

  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的

  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储

  • Web Support:Web 支持,可以非常容易的集成到Web 环境

  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率

  • Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去

  • Testing:提供测试支持

  • "Run As":允许一个用户假装为另一个用户(如果他们允许)的身份进行访问

  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

2.3、Shiro架构(外部)

从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作

image-20200729114702566

  • Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者

  • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色

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

2.4、Shiro架构(内部)

image-20200729114720578

  • Subject:任何可以与应用交互的“用户”;
  • SecurityManager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证、授权、会话及缓存的管理。
  • Authenticator:负责Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  • Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  • Realm:可以有1 个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的Realm;
  • SessionManager:管理Session 生命周期的组件;而Shiro并不仅仅可以用在Web 环境,也可以用在如普通的JavaSE环境 CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
  • Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密。

3、Hello World

3.1、快速实践

image-20200729115148574

  1. 创建一个maven父工程,用来学习Shiro,删掉不必要的部分

  2. 创建一个普通的Maven子工程:hell-shiro

image-20200729120114648

  1. 根据官方文档,我们导入Shiro的依赖

image-20200729120207730

版本号点击这里

       <dependencies>
           <dependency>
               <groupId>org.apache.shiro</groupId>
               <artifactId>shiro-core</artifactId>
               <version>1.9.0</version>
           </dependency>
   
           <!-- configure logging -->
           <dependency>
               <groupId>org.slf4j</groupId>
               <artifactId>jcl-over-slf4j</artifactId>
               <version>1.7.36</version>
           </dependency>
           <dependency>
               <groupId>org.slf4j</groupId>
               <artifactId>slf4j-log4j12</artifactId>
               <version>1.7.36</version>
           </dependency>
           <dependency>
               <groupId>log4j</groupId>
               <artifactId>log4j</artifactId>
               <version>1.2.17</version>
           </dependency>
       </dependencies>
  1. 相关配置文件

    • log4j.properties——官网
     log4j.rootLogger=INFO, stdout
         
     log4j.appender.stdout=org.apache.log4j.ConsoleAppender
     log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
     log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
         
     # General Apache libraries
     log4j.logger.org.apache=WARN
         
     # Spring
     log4j.logger.org.springframework=WARN
         
     # Default Shiro logging
     log4j.logger.org.apache.shiro=INFO
         
     # Disable verbose logging
     log4j.logger.org.apache.shiro.util.ThreadContext=WARN
     log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
    
     [users]
     # user 'root' with password 'secret' and the 'admin' role
     root = secret, admin
     # user 'guest' with the password 'guest' and the 'guest' role
     guest = guest, guest
     # user 'presidentskroob' with password '12345' ("That's the same combination on
     # my luggage!!!" ;)), and role 'president'
     presidentskroob = 12345, president
     # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
     darkhelmet = ludicrousspeed, darklord, schwartz
     # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
     lonestarr = vespa, goodguy, schwartz
         
     # -----------------------------------------------------------------------------
     # Roles with assigned permissions
     # 
     # Each line conforms to the format defined in the
     # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
     # -----------------------------------------------------------------------------
     [roles]
     # 'admin' role has all permissions, indicated by the wildcard '*'
     admin = *
     # The 'schwartz' role can do anything (*) with any lightsaber:
     schwartz = lightsaber:*
     # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
     # license plate 'eagle5' (instance specific id)
     goodguy = winnebago:drive:eagle5
    
    • 启动类 Quickstart——官网
     public class Quickstart {
         
         private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
         
         
         public static void main(String[] args) {
             // 加载配置文件
             IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
             DefaultSecurityManager securityManager = new DefaultSecurityManager();
             securityManager.setRealm(iniRealm);
         
         
             SecurityUtils.setSecurityManager(securityManager);
         
             // 获取当前的用户对象
             Subject currentUser = SecurityUtils.getSubject();
         
             // 通过当前用户获取session
             Session session = currentUser.getSession();
             session.setAttribute("someKey", "aValue");
             String value = (String) session.getAttribute("someKey");
             if (value.equals("aValue")) {
                 log.info("Retrieved the correct value! [" + value + "]");
             }
         
             // 判断当前用户是否被认证
             if (!currentUser.isAuthenticated()) {
                 // 设置token
                 UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                 token.setRememberMe(true);
                 try {
                     // 执行了登陆操作
                     currentUser.login(token);
                 } catch (UnknownAccountException uae) {
                     log.info("There is no user with username of " + token.getPrincipal());
                 } catch (IncorrectCredentialsException ice) {
                     log.info("Password for account " + token.getPrincipal() + " was incorrect!");
                 } catch (LockedAccountException lae) {
                     log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                             "Please contact your administrator to unlock it.");
                 }
                 // ... catch more exceptions here (maybe custom ones specific to your application?
                 catch (Exception e) {
                     //unexpected condition?  error?
                 }
             }
         
             //say who they are:
             //print their identifying principal (in this case, a username):
             log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
         
             // 测试角色
             if (currentUser.hasRole("schwartz")) {
                 log.info("May the Schwartz be with you!");
             } else {
                 log.info("Hello, mere mortal.");
             }
         
             //  粗粒度
             if (currentUser.isPermitted("lightsaber:wield")) {
                 log.info("You may use a lightsaber ring.  Use it wisely.");
             } else {
                 log.info("Sorry, lightsaber rings are for schwartz masters only.");
             }
         
             // 细粒度
             if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                 log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                         "Here are the keys - have fun!");
             } else {
                 log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
             }
         
             // 注销
             currentUser.logout();
         
             System.exit(0);
         }
     }
    

    image-20200729130649625

    • Spring Secutrry都有~(只是换了个名字)
     // 获取当前的用户对象 Subject
     Subject currentUser = SecurityUtils.getSubject();
     Session session = currentUser.getSession();
     currentUser.isAuthenticated()
         currentUser.getPrincipal()
         currentUser.hasRole("schwartz")
         currentUser.isPermitted("lightsaber:wield")
         currentUser.logout();
    

4、SpringBoot集成

4.1、SpringBoot整合Shiro环境搭建

  1. 新建一个项目或模块,勾选依赖

image-20200729174715011

pom.xml

   <dependencies>
       <!--thymeleaf-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-thymeleaf</artifactId>
       </dependency>
   
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
   
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
           <exclusions>
               <exclusion>
                   <groupId>org.junit.vintage</groupId>
                   <artifactId>junit-vintage-engine</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
   </dependencies>
  1. 测试环境是否正常

    • 新建一个controller页面
     @Controller
     public class MyController {
         
         @RequestMapping({"/","/index"})
         public String toIndex(Model model) {
             model.addAttribute("msg","hello,Shiro");
             return "index";
         }
             
         @RequestMapping("/user/add")
         public String add() {
             return "user/add";
         }
         
         @RequestMapping("/user/update")
         public String update() {
             return "user/update";
        }
     }
    
    • 新建一个index.html页面
     <!DOCTYPE html>
     <html lang="en" xmlns:th="http://www.thymeleaf.org">
     <head>
         <meta charset="UTF-8">
         <title>首页</title>
     </head>
     <body>
     <div>
         <h1>首页</h1>
         <p th:text="${msg}"></p>
         
         <hr>
         <a th:href="@{/user/add}">add</a>   | <a th:href="@{/user/update}">update</a>
     </div>
     </body>
     </html>
    
    • 新建一个add.html页面
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Title</title>
     </head>
     <body>
         <h1>add</h1>
     </body>
     </html>
    
    • 新建一个update.html页面
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Title</title>
     </head>
     <body>
         <h1>update</h1>
     </body>
     </html>
    
    • 项目结构

    image-20200729190325307

    • 运行截图

    image-20200729190548307

  2. 导入shiro整合spring的包——官网,查看最新版本

   <!--
         Subject  用户
         SecurityManager 管理所有用户
         Realm 连接数据库
   -->
   
   <!--shiro整合spring的包-->
   <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-spring</artifactId>
       <version>1.5.3</version>
   </dependency>
  1. 编写导入配置类

    • 编写一个自定义类UserRealm
     //自定义的UserRealm
     public class UserRealm extends AuthorizingRealm {
         //授权
         @Override
         protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
             System.out.println("执行了=>授权doGetAuthorizationInfo");
             return null;
         }
         
         //认证
         @Override
         protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
             System.out.println("执行了=>认证doGetAuthorizationInfo");
             return null;
         }
     }
    
    • 编写配置ShiroConfig

      • 创建realm对象,需要自定义类
      • DefaultWebSecurityManager
      • ShiroFilterFactoryBean
     @Configuration
     public class ShiroConfig {
         
         //3. shiroFilterFactoryBean
         
         @Bean
         public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
             ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
             // 设置安全管理器
             bean.setSecurityManager(defaultWebSecurityManager);
         
             return bean;
         }
         
         //2. DefaultWebSecurityManager
         
         @Bean
         public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
             DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
         
             // 关联userRealm
             securityManager.setRealm(userRealm);
             return securityManager;
         }
         //1. 创建realm对象,需要自定义类
         
         @Bean
         public UserRealm userRealm() {
             return new UserRealm();
         }
     }
    

4.2、Shiro实现登录拦截

  • ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

    • anon: 无需认证就可以访问
    • authc: 必须认证了才能访问
    • user: 必须拥有记住我功能才能用
    • perms: 拥有对某个资源的权限才能访问
    • role: 拥有某个角色权限
    Map<String, String> filterMap = new LinkedHashMap<>();
    filterMap.put("/user/add","authc");
    filterMap.put("/user/update","authc");
    bean.setFilterChainDefinitionMap(filterMap);
    
    • 点击首页的add或者update之后

    image-20200729191619576

    • 添加拦截成功页面
    • 登录页面login.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <h1>登录</h1>
    <hr>
    
    <form action="">
        <p>用户名:<input type="text" name="username"></p>
        <p>密码:<input type="text" name="password"></p>
        <p>密码:<input type="submit"></p>
    </form>
    </body>
    </html>
    
    • 在MyConfig中添加
      @RequestMapping("/toLogin")
      public String toLogin() {
          return "login";
      }
    
    • ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置
      //设置登录的请求
      bean.setLoginUrl("/toLogin");
    
  • 拦截成功页面

image-20200729192409085

4.3、Shiro实现用户认证

  1. MyController中编写用户提交表单之后处理
   @RequestMapping("/login")
   public String login(String username, String password, Model model) {
       //获取一个用户
       Subject subject = SecurityUtils.getSubject();
       // 封装用户的登录数据
       UsernamePasswordToken token = new UsernamePasswordToken(username, password);
   
       try {
           subject.login(token);//执行登录的方法,如果没有异常就说明ok了
           return "index";
       } catch (UnknownAccountException e) {//用户名不存在
           model.addAttribute("msg","用户名错误");
           return "login";
       } catch (IncorrectCredentialsException e) {//密码不存在
           model.addAttribute("msg","密码错误");
           return "login";
       }
   
   }
  1. login.html的修改
   <!DOCTYPE html>
   <html lang="en" xmlns:th="http://www.thymeleaf.org">
   <head>
       <meta charset="UTF-8">
       <title>登录页面</title>
   </head>
   <body>
   <h1>登录</h1>
   <hr>
   
   <p th:text="${msg}" style="color: red;"></p>
   <form th:action="@{/login}">
       <p>用户名:<input type="text" name="username"></p>
       <p>密码:<input type="text" name="password"></p>
       <p>密码:<input type="submit"></p>
   </form>
   </body>
   </html>
  1. 用户输入登录信息

    • 页面

    image-20200729220647520

    • 控制台

    image-20200729220926500

  2. 用户认证编写UserRealm中的认证(doGetAuthenticationInfo)

   //认证
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
       System.out.println("执行了=>认证doGetAuthorizationInfo");
       // 用户名、密码, 数据中取
       String name = "root";
       String password = "123456";
   
       UsernamePasswordToken userToken = (UsernamePasswordToken) token;
   
       if (!userToken.getUsername().equals(name)) {
           return null;//抛出异常 UnknownAccountException
       }
   
       // 密码认证,shiro做
       return new SimpleAuthenticationInfo("",password,"");
   }

4.4、Shiro整合Mybatis

  1. 导入依赖
   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
   </dependency>
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
   
   <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.17</version>
   </dependency>
   
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.1.23</version>
   </dependency>
   
   <!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的-->
   <dependency>
       <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
       <version>2.1.3</version>
   </dependency>
  1. 配置文件application.yml的编写
   spring:
     datasource:
       username: root
       password: admin
       #?serverTimezone=UTC解决时区的报错
       url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
       driver-class-name: com.mysql.cj.jdbc.Driver
       type: com.alibaba.druid.pool.DruidDataSource
   
       #Spring Boot 默认是不注入这些属性值的,需要自己绑定
       #druid 数据源专有配置
       initialSize: 5
       minIdle: 5
       maxActive: 20
       maxWait: 60000
       timeBetweenEvictionRunsMillis: 60000
       minEvictableIdleTimeMillis: 300000
       validationQuery: SELECT 1 FROM DUAL
       testWhileIdle: true
       testOnBorrow: false
       testOnReturn: false
       poolPreparedStatements: true
   
       #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
       #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
       #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
       filters: stat,wall,log4j
       maxPoolPreparedStatementPerConnectionSize: 20
       useGlobalDataSourceStat: true
       connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
       
   mybatis:
     type-aliases-package: nuc.ss.pojo
     mapper-locations: classpath:mapper/*.xml
  1. User类的编写
   @Data
   @AllArgsConstructor
   @NoArgsConstructor
   public class User {
       private int id;
       private String name;
       private String pwd;
   }
  1. UserMapper.xml映射
   <?xml version="1.0" encoding="UTF-8" ?>
   <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
   <!--namespace=绑定一个对应的Dao/Mapper接口-->
   <mapper namespace="nuc.ss.mapper.UserMapper">
       
       <select id="queryUserList" resultType="User">
           select * from mybatis.user;
       </select>
   
       <select id="queryUserById" resultType="User">
           select * from mybatis.user where id = #{id};
       </select>
   
       <insert id="addUser" parameterType="User">
           insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
       </insert>
   
       <update id="updateUser" parameterType="User">
           update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id};
       </update>
   
       <delete id="deleteUser" parameterType="int">
           delete from mybatis.user where id = #{id}
       </delete>
   </mapper>
  1. UserService接口实现
   public interface UserService {
   
       public User queryUserByName(String name);
   }
  1. UserServiceImpl业务逻辑
   @Service
   public class UserServiceImpl implements UserService {
   
       @Autowired
       UserMapper userMapper;
       @Override
       public User queryUserByName(String name) {
           return userMapper.queryUserByName(name);
       }
   }
   
  1. 测试环境
   @SpringBootTest
   class ShiroSpringbootApplicationTests {
   
       @Autowired
       UserService userService;
       @Test
       void contextLoads() {
           System.out.println(userService.queryUserByName("狂神"));
       }
   
   }

image-20200730121720922

  1. UserRealm连接真实数据库
   //认证
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
       System.out.println("执行了=>认证doGetAuthorizationInfo");
   
       UsernamePasswordToken userToken = (UsernamePasswordToken) token;
       
       // 真实数据库 用户名、密码, 数据中取
       User user = userService.queryUserByName(userToken.getUsername());
   
       if (user == null) {//没有这个人
           return null;
       }
   
       // 密码认证,shiro做
       return new SimpleAuthenticationInfo("",user.getPwd(),"");
   }

image-20200730180019861

  1. 断点测试密码加密类型

    • 打断点Debug

    image-20200730182621912

    • 默认是SimpleCredentialsMatcher加密

    image-20200730181814293

    123456——E10ADC3949BA59ABBE56E057F20F883E

    • MD5盐值加密

    • 所有加密

    image-20200730181944253

4.5、Shiro实现用户授权

  1. ShiroConfig中的getShiroFilterFactoryBean方法添加认证代码
   //授权,正常情况下,没有授权会跳转到为授权页面
   filterMap.put("/user/add","perms[user:add]");
   filterMap.put("/user/update","perms[user:update]");
  1. 登录之后点击add按钮会弹出如下页面

image-20200730195133631

  1. 添加为授权页面

    • MyController
     @RequestMapping("/noauto")
     @ResponseBody
     public String unauthorized() {
         return "未经授权,无法访问此页面";
     }
    
    • ShiroConfig中的getShiroFilterFactoryBean方法中添加
     //为授权页面
     bean.setUnauthorizedUrl("/noauto");
    
  2. 再次测试

image-20200730195807437

image-20200730195946692

所以需要在UserRealm中为用户进行真正授权

  1. UserRealm类的修改
   //自定义的UserRealm
   public class UserRealm extends AuthorizingRealm {
   
       @Autowired
       UserService userService;
       //授权
       @Override
       protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
           System.out.println("执行了=>授权doGetAuthorizationInfo");
   
           SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
   
           //拿到当前登录的这个对象
           Subject subject = SecurityUtils.getSubject();
           User currentUser = (User)subject.getPrincipal();//拿到user对象
   
           //设置当前用户的权限
           info.addStringPermission(currentUser.getPerms());
   
           return info;
       }
   
       //认证
       @Override
       protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
           ......
           // 密码认证,shiro做
           return new SimpleAuthenticationInfo(user,user.getPwd(),"");
       }
   }
   
  1. 再次测试

image-20200730202810034

4.6、Shiro整合Thymeleaf

  1. shiro-thymeleaf整合包导入——官网
   <!--shiro-thymeleaf整合-->
   <dependency>
       <groupId>com.github.theborakompanioni</groupId>
       <artifactId>thymeleaf-extras-shiro</artifactId>
       <version>2.0.0</version>
   </dependency>
  1. 在ShiroConfig中整合ShiroDialect
   // 整合ShiroDialect: 用来整合 Shiro thymeleaf
   @Bean
   public ShiroDialect getShiroDialect() {
       return new ShiroDialect();
   }
  1. index.html页面
   <!DOCTYPE html>
   <html lang="en" xmlns:th="http://www.thymeleaf.org"
         xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
   <head>
       <meta charset="UTF-8">
       <title>首页</title>
   </head>
   <body>
   
   <div>
       <h1>首页</h1>
       <p th:text="${msg}"></p>
   
       <!--用session实现,配合UserRealm中的session实现-->
       <!--<div th:if="${session.loginUser==null}">
           <a th:href="@{/toLogin}">登录</a>
       </div>-->
   
       <div shiro:notAuthenticated>
           <a th:href="@{/toLogin}">登录</a>
       </div>
   
       <hr>
   
       <div shiro:hasPermission="user:add">
           <a th:href="@{/user/add}">add</a>
       </div>
   
       <div shiro:hasPermission="user:update">
           <a th:href="@{/user/update}">update</a>
       </div>
   
   </div>
   </body>
   </html>
  1. 页面显示

image-20200730205736153

4.7、所有代码

  • ShiroConfig
  package nuc.ss.config;
  
  import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
  import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  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;
  
  import java.util.LinkedHashMap;
  import java.util.Map;
  
  @Configuration
  public class ShiroConfig {
  
      //shiroFilterFactoryBean
  
      @Bean
      public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
          ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
          // 设置安全管理器
          bean.setSecurityManager(defaultWebSecurityManager);
  
          // 添加shiro的内置过滤器
          /*
              anon: 无需认证就可以访问
              authc: 必须认证了才能访问
              user: 必须拥有记住我功能才能用
              perms: 拥有对某个资源的权限才能访问
              role: 拥有某个角色权限
           */
  
          //拦截
          Map<String, String> filterMap = new LinkedHashMap<>();
          //filterMap.put("/user/add","authc");
          //filterMap.put("/user/update","authc");
  
  
          //授权,正常情况下,没有授权会跳转到为授权页面
          filterMap.put("/user/add","perms[user:add]");
          filterMap.put("/user/update","perms[user:update]");
  
          filterMap.put("/user/*","authc");
  
          bean.setFilterChainDefinitionMap(filterMap);
  
          //设置登录的请求
          bean.setLoginUrl("/toLogin");
  
          //为授权页面
          bean.setUnauthorizedUrl("/noauto");
  
        return bean;
      }
  
      //DefaultWebSecurityManager
  
      @Bean
      public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
          DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  
          // 关联userRealm
          securityManager.setRealm(userRealm);
          return securityManager;
      }
      //创建realm对象,需要自定义类
  
      @Bean
      public UserRealm userRealm() {
          return new UserRealm();
      }
  
      // 整合ShiroDialect: 用来整合 Shiro thymeleaf
      @Bean
      public ShiroDialect getShiroDialect() {
          return new ShiroDialect();
      }
  }
  • UserRealm
  package nuc.ss.config;
  
  import nuc.ss.pojo.User;
  import nuc.ss.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.session.Session;
  import org.apache.shiro.subject.PrincipalCollection;
  import org.apache.shiro.subject.Subject;
  import org.springframework.beans.factory.annotation.Autowired;
  
  //自定义的UserRealm
  public class UserRealm extends AuthorizingRealm {
  
      @Autowired
      UserService userService;
      //授权
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
          System.out.println("执行了=>授权doGetAuthorizationInfo");
  
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  
          //info.addStringPermission("user:add");
  
          //拿到当前登录的这个对象
          Subject subject = SecurityUtils.getSubject();
          User currentUser = (User)subject.getPrincipal();//拿到user对象
  
          //设置当前用户的权限
          info.addStringPermission(currentUser.getPerms());
  
          return info;
      }
  
      //认证
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
          System.out.println("执行了=>认证doGetAuthorizationInfo");
  
          UsernamePasswordToken userToken = (UsernamePasswordToken) token;
  
          // 虚拟用户
          //String name = "root";
          //String password = "123456";
          //if (!userToken.getUsername().equals(name)) {
          //    return null;//抛出异常 UnknownAccountException
          //}
  
          // 真实数据库 用户名、密码, 数据中取
          User user = userService.queryUserByName(userToken.getUsername());
  
          if (user == null) {//没有这个人
              return null;
          }
  
          //首页
          //Subject currentSubject = SecurityUtils.getSubject();
          //Session session = currentSubject.getSession();
          //session.setAttribute("loginUser",user);
  
  
          // 密码认证,shiro做
          return new SimpleAuthenticationInfo(user,user.getPwd(),"");
      }
  }
  • MyController
  package nuc.ss.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.stereotype.Controller;
  import org.springframework.ui.Model;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.ResponseBody;
  
  @Controller
  public class MyController {
  
      @RequestMapping({"/","/index"})
      public String toIndex(Model model) {
          model.addAttribute("msg","hello,Shiro");
          return "index";
      }
  
      @RequestMapping("/user/add")
      public String add() {
          return "user/add";
      }
  
      @RequestMapping("/user/update")
      public String update() {
          return "user/update";
      }
  
      @RequestMapping("/toLogin")
      public String toLogin() {
          return "login";
      }
  
      @RequestMapping("/login")
      public String login(String username, String password, Model model) {
          //获取一个用户
          Subject subject = SecurityUtils.getSubject();
          // 封装用户的登录数据
          UsernamePasswordToken token = new UsernamePasswordToken(username, password);
  
          try {
              subject.login(token);//执行登录的方法,如果没有异常就说明ok了
              return "index";
          } catch (UnknownAccountException e) {//用户名不存在
              model.addAttribute("msg","用户名错误");
              return "login";
          } catch (IncorrectCredentialsException e) {//密码不存在
              model.addAttribute("msg","密码错误");
              return "login";
          }
      }
  
      @RequestMapping("/noauto")
      @ResponseBody
      public String unauthorized() {
          return "未经授权,无法访问此页面";
      }
  }
  
  • pom依赖
  <?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>nuc.ss</groupId>
      <artifactId>shiro-springboot</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>shiro-springboot</name>
      <description>Demo project for Spring Boot</description>
  
      <properties>
          <java.version>1.8</java.version>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
          <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
      </properties>
  
      <dependencies>
  
          <!--
              Subject  用户
              SecurityManager 管理所有用户
              Realm 连接数据库
          -->
  
          <!--shiro-thymeleaf整合-->
          <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
          <dependency>
              <groupId>com.github.theborakompanioni</groupId>
              <artifactId>thymeleaf-extras-shiro</artifactId>
              <version>2.0.0</version>
          </dependency>
  
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
          </dependency>
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
          </dependency>
  
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
  
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.1.23</version>
          </dependency>
  
          <!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的-->
          <dependency>
              <groupId>org.mybatis.spring.boot</groupId>
              <artifactId>mybatis-spring-boot-starter</artifactId>
              <version>2.1.3</version>
          </dependency>
  
          <!--shiro整合spring的包-->
          <dependency>
              <groupId>org.apache.shiro</groupId>
              <artifactId>shiro-spring</artifactId>
              <version>1.5.3</version>
          </dependency>
          <!--thymeleaf-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-thymeleaf</artifactId>
          </dependency>
  
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
  
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <scope>test</scope>
              <exclusions>
                  <exclusion>
                      <groupId>org.junit.vintage</groupId>
                      <artifactId>junit-vintage-engine</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
      </dependencies>
  
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-dependencies</artifactId>
                  <version>${spring-boot.version}</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
  
      <build>
          <plugins>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <configuration>
                      <source>1.8</source>
                      <target>1.8</target>
                      <encoding>UTF-8</encoding>
                  </configuration>
              </plugin>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>
  
  </project>
  

5、完美的解释

让 Apache Shiro 保护你的应用