欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

狂神说笔记——SpringBoot操作数据库22-3

时间:2023-06-30
SpringBoot操作数据库(3) 5.整合Shiro 1.Shiro简介 1.什么是Shiro?

Apache Shiro 是一个Java 的安全(权限)框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在Java EE环
境。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。下载地址:http://shiro.apache.org/ 2.有哪些功能?

Authentication:身份认证、登录,验证用户是不是拥有相应的身份;Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;Web Support:Web支持,可以非常容易的集成到Web环境;Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率Concurrency:Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传播过去Testing:提供测试支持;Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。 3.Shiro架构(外部)

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

subject: 应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject, Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其 实是一个门面,SecurityManageer 才是实际的执行者SecurityManager:安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它 管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于 SpringMVC的DispatcherServlet的角色Realm:Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager 要验证 用户身份,那么它需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从 Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看成 DataSource; 4.Shiro架构(内部)

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

查看官网文档:http://shiro.apache.org/tutorial.html官方的quickstart:https://github.com/apache/shiro/tree/master/samples/quickstart/ 1.快速实践 创建一个maven父工程,用于学习Shiro,删掉不必要的东西。创建一个普通的Maven子工程:shiro-01-helloworld。根据官方文档,导入Shiro的依赖。

org.apache.shiro shiro-core 1.8.0 org.slf4j jcl-over-slf4j 2.0.0-alpha1 org.slf4j slf4j-log4j12 2.0.0-alpha1 org.apache.logging.log4j log4j-core 2.17.1

编写Shiro配置——log4j.properties。

log4j.rootLogger=INFO, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n# General Apache librarieslog4j.logger.org.apache=WARN# Springlog4j.logger.org.springframework=WARN# Default Shiro logginglog4j.logger.org.apache.shiro=INFO# Disable verbose logginglog4j.logger.org.apache.shiro.util.ThreadContext=WARNlog4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

shiro.ini

[users]# user 'root' with password 'secret' and the 'admin' roleroot = secret, admin# user 'guest' with the password 'guest' and the 'guest' roleguest = 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

编写自己的QuickStrat

import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;//import org.apache.shiro.ini.IniSecurityManagerFactory;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.lang.util.Factory;import org.apache.shiro.util.Factory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;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 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: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) 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()) { 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); }}

测试运行一下。

发现,执行完毕什么都没有,可能是maven依赖中的作用域问题,我们需要将scope作用域删掉,
默认是在test,然后重启,那么我们的quickstart就结束了,默认的日志消息!

[main] INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler..、[main] INFO [Quickstart] - Retrieved the correct value! [aValue] [main] INFO [Quickstart] - User [lonestarr] logged in successfully、[main] INFO [Quickstart] - May the Schwartz be with you! [main] INFO [Quickstart] - You may use a lightsaber ring、 Use it wisely、[main] INFO [Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'、 Here are the keys - have fun!

OK! 2.代码解释 导入了一堆包!类的描述

通过工厂模式创建SecurityManager的实例对象。

// 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:// 使用类路径根目录下的shiro.ini文件// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively):Factory factory = newIniSecurityManagerFactory("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);// 现在已经建立了一个简单的Shiro环境,让我们看看您可以做什么:// Now that a simple Shiro environment is set up, let's see what you cando:

获取当前的Subject

// get the currently executing user: 获取当前正在执行的用户Subject currentUser = SecurityUtils.getSubject();

session的操作

// 用会话做一些事情(不需要web或EJB容器!!!)// Do some stuff with a Session (no need for a web or EJB container!!!)Session session = currentUser.getSession(); //获得sessionsession.setAttribute("someKey", "aValue"); //设置Session的值!String value = (String) session.getAttribute("someKey"); //从session中获取值if (value.equals("aValue")) { //判断session中是否存在这个值! log.info("==Retrieved the correct value! [" + value + "]");}

用户认证功能

// 测试当前的用户是否已经被认证,即是否已经登录!// let's login the current user so we can check against roles andpermissions:if (!currentUser.isAuthenticated()) { // isAuthenticated();是否认证 //将用户名和密码封装为 UsernamePasswordToken ; UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); //记住我功能 try { currentUser.login(token); //执行登录,可以登录成功的! } catch (UnknownAccountException uae) { //如果没有指定的用户,则UnknownAccountException异常 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 toyour 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 licenseplate (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);

OK,一个简单的Shiro程序体验结束!!!

3.集成shiro 1.准备工作 搭建一个SpringBoot项目、选中web模块即可!导入Maven依赖thymeleaf

org.thymeleaf thymeleaf-spring5 org.thymeleaf.extras thymeleaf-extras-java8time

编写一个页面 index.html——templates

首页

首页哦!

编写controller进行访问测试。

package com.github.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","Hello,Shiro!"); return "index"; }}

访问测试! 2.整合Shiro

回顾核心API:

Subject:用户主体SecurityManager:安全管理器Realm:Shiro 连接数据

步骤:

导入Shiro 和 spring整合的依赖。

org.apache.shiro shiro-spring 1.4.1

编写Shiro 配置类——config包

package com.github.config;import org.springframework.context.annotation.Configuration;// 声明为配置类@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean // 创建 DefaultWebSecurityManager // 创建 realm 对象}

创建一个realm对象,需要自定义一个realm的类,用来编写一些查询的方法,或者认证与授权的逻辑。

package com.github.config;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;// 自定义Realmpublic class UserRealm extends AuthorizingRealm { // 执行授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行 => 授权逻辑PrincipalCollection"); return null; } // 执行认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了 => 认证逻辑AuthenticationToken"); return null; }}

将这个类注册到我们的Bean中!——ShiroConfig

@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean // 创建 DefaultWebSecurityManager // 创建 realm 对象 @Bean public UserRealm userRealm(){ return new UserRealm(); }}

创建DefaultWebSecurityManager

// 创建 DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联Realm securityManager.setRealm(userRealm); return securityManager; }

创建ShiroFilterFactoryBean

// 创建 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; }

完整的配置:

package com.github.config;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;// 声明为配置类@Configurationpublic class ShiroConfig { // 创建 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; } // 创建 DefaultWebSecurityManager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联Realm securityManager.setRealm(userRealm); return securityManager; } // 创建 realm 对象 @Bean public UserRealm userRealm(){ return new UserRealm(); }}

3.页面拦截实现 编写两个页面、在templates目录下新建一个user目录add.html、update.html

ADD

ADD

update

update

编写跳转到页面的controller

@Controllerpublic class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","Hello,Shiro!"); return "index"; } @RequestMapping("/user/add") public String toAdd(){ return "user/add"; } @RequestMapping("/user/update") public String toUpdate(){ return "user/update"; }}

在index页面上,增加跳转链接

首页哦!


add | update

测试页面跳转是否OK 准备添加Shiro的内置过滤器

// 创建 ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); Map filterMap = new linkedHashMap(); filterMap.put("/user/add","authc"); filterMap.put("/user/update","authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean;}

再起启动测试,访问链接进行测试!拦截OK!但是发现,点击后会跳转到一个Login.jsp页面,这 个不是我们想要的效果,我们需要自己定义一个Login页面! 编写一个个人的Login.html页面

登录

登录


编写跳转的controller

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

在shiro中配置一下! ShiroFilterFactoryBean() 方法下面 再次测试,成功的跳转到了我们指定的Login页面! 优化一下,使用通配符来操作! 测试一下! 4.登录认证操作 编写登录的controller

// 登陆操作 @RequestMapping("/login") public String login(String username,String password,Model model){ // 使用shiro,编写认证操作 // 1、获取Subject Subject subject = SecurityUtils.getSubject(); // 2、封装用户的数据 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 3、执行登录的方法,只要没有异常就代表登录成功! try { // 登录成功!返回首页 subject.login(token); return "index"; // 用户名不存在 } catch (UnknownAccountException e) { model.addAttribute("msg","用户名不存在"); return "login"; // 密码错误 } catch (IncorrectCredentialsException e) { model.addAttribute("msg","密码错误"); return "login"; } }

在前端修改对应的信息输出或者请求!登录页面增加一个msg提示:

给表单增加一个提交地址:

测试一下: 在UserRealm中编写用户认证的判断逻辑:

// 执行认证逻辑@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了 => 认证逻辑AuthenticationToken"); // 数据库的用户名和密码 String name = "root"; String password = "root"; // 1.判断用户名 UsernamePasswordToken userToken = (UsernamePasswordToken)token; if (!userToken.getUsername().equals(name)){ // 用户名不存在 // shiro底层就会抛出 UnknownAccountException return null; } // 2、验证密码,我们可以使用一个AuthenticationInfo实现类SimpleAuthenticationInfo // shiro会自动帮我们验证!重点是第二个参数就是要验证的密码! return new SimpleAuthenticationInfo("", password, "");}

测试一下! 5.整合数据库

CREATE DATAbase `mybatis`;USE `mybatis`;CREATE TABLE `user`(`id` INT(20) NOT NULL PRIMARY KEY,`name` VARCHAR(30) DEFAULT NULL,`pwd` VARCHAR(30) DEFAULT NULL)ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `user`(`id`,`name`,`pwd`) VALUES(1,'subei','123456'),(2,'张三','123456'),(3,'李四','123456');

导入Mybatis相关依赖

org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.0 mysql mysql-connector-java runtime log4j log4j 1.2.17 com.alibaba druid 1.1.12

编写配置文件-连接配置——application.yml

spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #SpringBoot默认是不注入这些的,需要自己绑定 #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.Properity #则导入log4j 依赖就行 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

编写mybatis的配置——application.properties

# 别名配置mybatis.type-aliases-package=com.github.pojomybatis.mapper-locations=classpath:mapper/*.xml

编写实体类,引入Lombok

org.projectlombok lombok 1.18.22

@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private int id; private String name; private String pwd;}

编写Mapper接口

@Repository@Mapperpublic interface UserMapper { public User queryUserByName(String name);}

编写Mapper配置文件

<?xml version="1.0" encoding="UTF-8" ?> select * from user where name = #{name}

编写UserService 层

public interface UserService { public User queryUserByName(String name);}

@Servicepublic class UserServiceImpl implements UserService{ @Autowired UserMapper mapper; @Override public User queryUserByName(String name) { return mapper.queryUserByName(name); }}

测试一下,保证能够从数据库中查询出来。

@SpringBootTestclass SpringbootShiroApplicationTests { @Autowired UserServiceImpl userService; @Test void contextLoads() { System.out.println(userService.queryUserByName("root")); }}

改造UserRealm,连接到数据库进行真实的操作!

// 自定义Realmpublic class UserRealm extends AuthorizingRealm { @Autowired UserService userService; // 执行授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); return null; } // 执行认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("执行了=>认证逻辑AuthenticationToken"); UsernamePasswordToken userToken = (UsernamePasswordToken)token; // 真实连接数据库 User user = userService.queryUserByName(userToken.getUsername()); if (user==null){ // 用户名不存在 return null; // shiro底层就会抛出 UnknownAccountException } return new SimpleAuthenticationInfo("", user.getPwd(), ""); }}

测试成功!

思考:密码比对原理探究

这个Shiro,是怎么帮我们实现密码自动比对的呢?

去 realm的父类 AuthorizingRealm 的父类 AuthenticatingRealm 中找一个方法;核心: getCredentialsMatcher() 翻译过来:获取证书匹配器;去看这个接口 CredentialsMatcher 有很多的实现类,MD5盐值加密;

密码一般都不能使用明文保存?

需要加密处理;思路分析

如何把一个字符串加密为MD5;替换当前的Realm 的 CredentialsMatcher 属性,直接使用 Md5CredentialsMatcher 对象, 并设置加密算法;

// 密码验证// 加密 md5HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 加密算法的名称hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 是否让它 进行16进制的编码hashedCredentialsMatcher.isStoredCredentialsHexEncoded();// 迭代的次数// hashedCredentialsMatcher.setHashIterations(3);SimpleHash simpleHash = new SimpleHash("MD5",user.getPwd() );String s = simpleHash.toHex();return new SimpleAuthenticationInfo("",s,"");

6.用户授权操作

使用shiro的过滤器来拦截请求即可!

在ShiroFilterFactoryBean中添加一个过滤器:

// 授权过滤器filterMap.put("/user/add","perms[user:add]"); // 大家记得注意顺序!

再次启动测试一下,访问add,发现以下错误!未授权错误!

注意:当我们实现权限拦截后,shiro会自动跳转到未授权的页面,但没有这个页面,所有401了;

配置一个未授权的提示的页面,增加一个controller提示;

@RequestMapping("/noauth")@ResponseBodypublic String noAuth(){ return "未经授权不能访问此页面";}

然后再 shiroFilterFactoryBean 中配置一个未授权的请求页面!

shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

测试,现在没有授权,可以跳转到我们指定的位置了! 7.用户授权操作

在UserRealm 中添加授权的逻辑,增加授权的字符串!

// 执行授权逻辑@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); // 给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加资源的授权字符串 info.addStringPermission("user:add"); return info;}

再次登录测试,发现登录的用户是可以进行访问add 页面了!授权成功!

问题,我们现在完全是硬编码,无论是谁登录上来,都可以实现授权通过,但是真实的业务情况应该 是,每个用户拥有自己的一些权限,从而进行操作,所以说,权限,应该在用户的数据库中,正常的情 况下,应该数据库中是由一个权限表的,我们需要联表查询,但是这里为了大家操作理解方便一些,我 们直接在数据库表中增加一个字段来进行操作!

修改实体类,增加一个字段

@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private int id; private String name; private String pwd; private String perms;}

在自定义的授权认证中,获取登录的用户,从而实现动态认证授权操作!

在用户登录授权的时候,将用户放在 Principal 中,改造下之前的代码

return new SimpleAuthenticationInfo(user, user.getPwd(), "");

然后再授权的地方获得这个用户,从而获得它的权限

// 执行授权逻辑@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("执行了=>授权逻辑PrincipalCollection"); // 给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加资源的授权字符串 // info.addStringPermission("user:add"); // 获得当前对象 Subject subject = SecurityUtils.getSubject(); // 拿到User对象 User currentUser = (User) subject.getPrincipal(); // 设置权限 info.addStringPermission(currentUser.getPerms()); return info;}

给数据库中的用户增加一些权限 在过滤器中,将update请求也进行权限拦截下

启动测试,登录不同的账户,进行测试一下!测试完美通过OK! 8.整合Thymeleaf

根据权限展示不同的前端页面

添加Maven的依赖;

com.github.theborakompanioni thymeleaf-extras-shiro 2.0.0

配置一个shiro的Dialect ,在shiro的配置中增加一个Bean

// 配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用@Beanpublic ShiroDialect getShiroDialect(){ return new ShiroDialect();}

修改前端的配置

首页

首页哦!


add update

测试一下,可以发现,现在首页什么都没有了,因为我们没有登录,我们可以尝试登录下,来判断这个Shiro的效果!登录后,可以看到不同的用户,有不同的效果,现在就已经接近完美了~!但还有问题! 在用户登录后应该把信息放到Session中,我们完善下!在执行认证逻辑时候,加入session。

Subject subject = SecurityUtils.getSubject();subject.getSession().setAttribute("loginUser",user);

前端从session中获取,然后用来判断是否显示登录。

登录

测试一下!

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。