1、Shiro简介

1.1、Shiro 是什么?

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

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

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

  • 下载地址

    • 官网:http://shiro.apache.org/
    • github:https://github.com/apache/shiro

1.2、有哪些功能?

SpringBoot之集成Shiro_Shiro

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

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

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

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

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

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

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

  • Testing:提供测试支持

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

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

1.3、Shiro架构(外部)

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

SpringBoot之集成Shiro_Shiro_02

  • 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

1.4、Shiro架构(内部)

SpringBoot之集成Shiro_Shiro_03

  • 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提高了一些常见的加密组件用于如密码加密/解密。
2、Hello World

2.1、快速实践

  • 查看官方文档:http://shiro.apache.org/tutorial.html

  • 官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/

    SpringBoot之集成Shiro_Shiro_04

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

  2. 创建一个普通的Maven子工程:Hello-Shiro

    SpringBoot之集成Shiro_SpringBoot_05

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

    SpringBoot之集成Shiro_SpringBoot_06

    版本号点击这里

    <dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.6.0</version></dependency><!-- configure logging --><!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j --><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.0-alpha1</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>2.0.0-alpha1</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies>
  4. 相关配置文件

  • 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
  • shiro.ini——官网

    [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——官网

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.Session;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/**
     * Simple Quickstart application showing how to use Shiro's API.
     * 简单入门Shiro使用API
     *
     * @since 0.9 RC2
     */public class Quickstart {private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {// The easiest way to create a Shiro SecurityManager with configured// realms, users, roles and permissions is to use the simple INI config.// We'll do that by using a factory that can ingest a .ini file and// return a SecurityManager instance:// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively):// 读取配置文件:Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();// for this simple example quickstart, make the SecurityManager// accessible as a JVM singleton.  Most applications wouldn't do this// and instead rely on their container configuration or web.xml for// webapps.  That is outside the scope of this simple quickstart, so// we'll just do the bare minimum so you can continue to get a feel// for things.SecurityUtils.setSecurityManager(securityManager);// Now that a simple Shiro environment is set up, let's see what you can do:// get the currently executing user:// 获取当前的用户对象 SubjectSubject currentUser = SecurityUtils.getSubject();// Do some stuff with a Session (no need for a web or EJB container!!!)//通过当前用户拿到Shiro的Session 可以脱离web存值取值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 + "]");}// let's login the current user so we can check against roles and permissions://判断当前的用户是否被认证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 (AuthenticationException ae) {//unexpected condition?  error?}}//say who they are://print their identifying principal (in this case, a username):log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");//test a role:// 检查角色if (currentUser.hasRole("schwartz")) {log.info("May the Schwartz be with you!");} else {log.info("Hello, mere mortal.");}//test a typed permission (not instance-level)//粗粒度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.");}//a (very powerful) Instance Level permission://细粒度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!");}//all done - log out!//注销currentUser.logout();//结束System.exit(0);}}

    SpringBoot之集成Shiro_Shiro_07

  • Spring Secutrry都有~(只是换了个名字)

    // 获取当前的用户对象 SubjectSubject currentUser = SecurityUtils.getSubject();Session session = currentUser.getSession();currentUser.isAuthenticated()currentUser.getPrincipal()currentUser.hasRole("schwartz")currentUser.isPermitted("lightsaber:wield")currentUser.logout();
3、SpringBoot集成

3.1、SpringBoot整合Shiro环境搭建

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

    SpringBoot之集成Shiro_Shiro_08

    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>
  2. 测试环境是否正常

  • 新建一个controller页面

    @Controllerpublic 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>
  • 项目结构

    SpringBoot之集成Shiro_SpringBoot_09

  • 运行截图

    SpringBoot之集成Shiro_SpringBoot_10

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

<!--
      Subject  用户
      SecurityManager 管理所有用户
      Realm 连接数据库
--><!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.6.0</version></dependency>

编写导入配置类

  • 创建realm对象,需要自定义类
  • DefaultWebSecurityManager
  • ShiroFilterFactoryBean
  • 编写一个自定义类UserRealm

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

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

3.2、Shiro实现登录拦截

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

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

    SpringBoot之集成Shiro_SpringBoot_11

  • 添加拦截成功页面

    • 登录页面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>
    • 在MyController中添加

      @RequestMapping("/toLogin")public String toLogin() {return "login";}
    • 在ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

      //设置登录的请求bean.setLoginUrl("/toLogin");
  • 拦截成功页面

    SpringBoot之集成Shiro_SpringBoot_12

3.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";}}
  2. 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><input type="submit"></form></body></html>
  3. 用户输入登录信息

  • 页面

    SpringBoot之集成Shiro_Shiro_13

  • 控制台

    SpringBoot之集成Shiro_Shiro_14

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

//认证@Overrideprotected 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,"");}

3.4、Shiro整合Mybatis

  1. 导入依赖

    <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</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.2.1</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>
  2. 配置文件application.yml的编写

    spring:
      datasource:username: rootpassword: root#?serverTimezone=UTC解决时区的报错url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#Spring Boot 默认是不注入这些属性值的,需要自己绑定#druid 数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4jfilters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500    
    mybatis:
      type-aliases-package: com.zhy.pojo  mapper-locations: classpath:mapper/*.xml
  3. User类的编写

    @Data@AllArgsConstructor@NoArgsConstructorpublic class User {private int id;private String name;private String pwd;}
  4. UserMapper接口编写

    package com.zhy.mapper;import com.zhy.pojo.User;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Repository;@Repository@Mapperpublic interface UserMapper {public User queryUserByName(String name);}
  5. 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>
  6. UserService接口实现

    public interface UserService {public User queryUserByName(String name);}
  7. UserServiceImpl业务逻辑

    @Servicepublic class UserServiceImpl implements UserService {@AutowiredUserMapper userMapper;@Overridepublic User queryUserByName(String name) {return userMapper.queryUserByName(name);}}
  8. 测试环境

    @SpringBootTestclass ShiroSpringbootApplicationTests {@AutowiredUserServiceImpl userService;@Testvoid contextLoads() {System.out.println(userService.queryUserByName("zhy"));}}

    SpringBoot之集成Shiro_Shiro_15

  9. UserRealm连接真实数据库

    //认证@Overrideprotected 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(),"");}

    SpringBoot之集成Shiro_Shiro_16

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

  • 打断点Debug

    SpringBoot之集成Shiro_SpringBoot_17

  • 默认是SimpleCredentialsMatcher加密

    SpringBoot之集成Shiro_SpringBoot_18

  • MD5加密——测试

    123456——E10ADC3949BA59ABBE56E057F20F883E

  • MD5盐值加密

  • 所有加密

    SpringBoot之集成Shiro_SpringBoot_19

3.5、Shiro实现用户授权

  1. ShiroConfig中的getShiroFilterFactoryBean方法添加认证代码

    //授权,正常情况下,没有授权会跳转到为授权页面filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");
  2. 登录之后点击add按钮会弹出如下页面

    SpringBoot之集成Shiro_SpringBoot_20

  3. 添加未授权页面

  • MyController

    @RequestMapping("/noauth")@ResponseBodypublic String unauthorized() {return "未经授权,无法访问此页面";}
  • ShiroConfig中的getShiroFilterFactoryBean方法中添加

    //为授权页面bean.setUnauthorizedUrl("/noauth");

再次测试

SpringBoot之集成Shiro_Shiro_21

SpringBoot之集成Shiro_Shiro_22

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

给用户表增加权限字段,并修改pojo

SpringBoot之集成Shiro_Shiro_23

设置相应权限

SpringBoot之集成Shiro_SpringBoot_24

UserRealm类的修改

//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {@AutowiredUserService userService;//授权@Overrideprotected 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;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {......// 密码认证,shiro做return new SimpleAuthenticationInfo(user,user.getPwd(),"");}}

再次测试

SpringBoot之集成Shiro_Shiro_25

3.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>
  2. 在ShiroConfig中整合ShiroDialect

    // 整合ShiroDialect: 用来整合 Shiro thymeleaf@Beanpublic ShiroDialect getShiroDialect() {return new ShiroDialect();}
  3. 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>
  4. 页面显示

    SpringBoot之集成Shiro_Shiro_26

3.7、所有代码

  • ShiroConfig

    package com.zhy.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;@Configurationpublic class ShiroConfig {//3. shiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 设置安全管理器bean.setSecurityManager(defaultWebSecurityManager);/*
            - anon: 无需认证就可以访问
            - authc: 必须认证了才能访问
            - user: 必须拥有记住我功能才能用
            - perms: 拥有对某个资源的权限才能访问
            - role: 拥有某个角色权限
             */Map<String, String> filterMap = new LinkedHashMap<>();// filterMap.put("/user/add","authc");// filterMap.put("/user/update","authc");bean.setFilterChainDefinitionMap(filterMap);//设置登录的请求bean.setLoginUrl("/toLogin");bean.setUnauthorizedUrl("/noauth");//授权,正常情况下,没有授权会跳转到为授权页面filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");return bean;}//2. DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 关联userRealmsecurityManager.setRealm(userRealm);return securityManager;}//1. 创建realm对象,需要自定义类@Beanpublic UserRealm userRealm() {return new UserRealm();}// 整合ShiroDialect: 用来整合 Shiro thymeleaf@Beanpublic ShiroDialect getShiroDialect() {return new ShiroDialect();}}
  • UserRealm

    package com.zhy.config;import com.zhy.pojo.User;import com.zhy.service.UserServiceImpl;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 org.springframework.beans.factory.annotation.Autowired;//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {@AutowiredUserServiceImpl userService;//授权@Overrideprotected 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;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthorizationInfo");UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;// 真实数据库 用户名、密码, 数据中取User user = userService.queryUserByName(userToken.getUsername());if (user == null) {//没有这个人return null;}// 密码认证,shiro做return new SimpleAuthenticationInfo(user,user.getPwd(),"");}}
  • MyController

    package com.zhy.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;@Controllerpublic 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("/noauth")@ResponseBodypublic 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><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.zhy</groupId><artifactId>springboot_07_shiro</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot_07_shiro</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</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.2.1</version></dependency><!--shiro-thymeleaf整合--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</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><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><!--shiro整合spring的包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.6.0</version></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><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
4、完美的解释

让 Apache Shiro 保护你的应用