SpringBoot 与Shiro 整合- 用户认证与授权实战

观看 黑马的SpringBoot与Shiro整合-权限管理实战视频,根据视频学习 一步一步 实现 SpringBoot +Shiro 整合实现用户认证和权限管理。简单易懂,适合新手学习,分享给大家。

SpringBoot 与Shiro 整合实现用户认证、用户授权

目录

  • 一、SpringBoot 与Shiro 整合实现用户认证(登录)
    • 1.分析shiro的核心API
    • 2.SpringBoot 整合Shiro步骤
      • 2.1 导入 shiro 与spring 整合依赖
      • 2.2 自定义Realm
      • 2.3 编写Shiro配置类
    • 3.使用Shiro内置的过滤器实现认证资源拦截
    • 4.实现用户认证(登录)操作
      • 4.1 设计登录页面
      • 4.2 编写Controller的登录逻辑
      • 4.3编写Realm的判断逻辑
    • 5.整合MyBatis实现登录
      • 5.1 导入mybatis相关的依赖
      • 5.2 配置application.properties
      • 5.3 编写 User实体类
      • 5.4 编写 UserMapper接口
      • 5.5 编写 UserMapper.xml映射文件
      • 5.6 编写业务接口和实现
      • 5.7 添加@MapperScan注解
      • 5.8 修改 UserRealm(自定义Realm类)
      • 5.8 测试
  • 二、SpringBoot 与Shiro 整合实现用户授权
    • 1.使用Shiro内置过滤器实现授权页面拦截(授权过滤器)
    • 2.完成Shiro的资源授权(授权字符串写死)
    • 3.关联数据库,动态授权
      • 3.1 数据库表简单处理:修改用户表,新增一个字段perms,来存储用户的资源权限
      • 3.2 User实体修改
      • 3.3 改造UserRealm类中的授权方法
      • 3.4 测试
  • 三、thymeleaf和shiro标签整合使用
    • 1.导入thymeleaf扩展坐标
    • 2.配置ShiroDialect
    • 3.在页面上使用shiro标签
  • 四、完整代码

一、SpringBoot 与Shiro 整合实现用户认证(登录)

1.分析shiro的核心API

Subject:用户主体(把操作交给SecurityManager
SecurityManager:安全管理器(关联Realm
RealmShiro连接数据的桥梁

2.SpringBoot 整合Shiro步骤

2.1 导入 shiro 与spring 整合依赖

pom.xml:

    <!-- 导入web支持:SpringMVC开发支持,Servlet相关的程序 -->
    <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.4.0</version>
    </dependency>

2.2 自定义Realm

package com.koncord.shiro;

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;

/**
 * 自定义 Realm
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{
     

	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
		System.out.println("执行授权逻辑");
		return null;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
		System.out.println("执行认证逻辑");
		return null;
	}

}

2.3 编写Shiro配置类

package com.koncord.shiro;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;

/**
 * Shiro配置类
 * @author Administrator
 *
 */
@Configuration
public class ShiroConfig {
     

	/**
	 * 3.创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
     
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 2.创建 DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("userRealm")UserRealm userRealm){
     
		DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
		//关联realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 1.创建Realm
	 */
	@Bean
	public UserRealm userRealm(){
     
		return new UserRealm();
	}
}

3.使用Shiro内置的过滤器实现认证资源拦截

修改 配置类ShiroConfig 中的 shiroFilterFactoryBean方法:

	/**
	 * 3.创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
     
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截
		 *    常用的过滤器:
		 *       anon:无需认证(登录)可以访问
		 *       authc:必须认证才可以访问
		 *       user:如果使用rememberMe的功能可以直接访问
		 *       perms:该资源必须得到资源权限才可以访问
		 *       role:该资源必须得到角色权限才可以访问
		 */
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
//		filterMap.put("/add", "authc");
//		filterMap.put("/update", "authc");
		filterMap.put("/testThymeleaf", "anon");
		filterMap.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		//设置登录跳转链接
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		return shiroFilterFactoryBean;
	}

项目结构:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第1张图片
创建一个UserController类,用于测试 上面的 代码效果:

package com.koncord.controller;

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 UserController {
     

	/**
	 * 测试
	 */
	@RequestMapping("/hello")
	@ResponseBody
	public String  hello(){
     
		System.out.println("UserController-hello");
		return "ok";
	}
	
	/**
	 * 测试 thymeleaf
	 */
	@RequestMapping("/testThymeleaf")
	public String  testThymeleaf(Model model){
     
		//将数据存入model
		model.addAttribute("name", "程序猿");
		//返回test.html
		return "test";
	}
	
	/**
	 * 进入添加页面
	 */
	@RequestMapping("/add")
	public String  add(){
     
		return "user/add";
	}
	
	/**
	 * 进入编辑页面
	 */
	@RequestMapping("/update")
	public String  update(){
     
		return "user/update";
	}
	
	/**
	 * 进入登录页面
	 */
	@RequestMapping("/toLogin")
	public String  toLogin(){
     
		return "login";
	}
}

test.html:


<html>
  <head>
    <title>测试thymeleaf的使用title>
	
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    
    

  head>
  
  <body>
    <h3 th:text="${name}">h3><br>
    <div>进入用户添加页面:<a href="add">用户添加a>div>
    <div>进入用户编辑页面:<a href="update">用户编辑a>div>
  body>
html>

测试效果:SpringBoot 与Shiro 整合- 用户认证与授权实战_第2张图片

4.实现用户认证(登录)操作

4.1 设计登录页面


<html>
  <head>
    <title>登录title>
	
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    
    

  head>
  
  <body>
    <h3>登录h3>
    <h3 th:text="${msg}" style="color:red;">h3>
    <form method="post" action="login">
       <div>用户名:<input type="text" name="userName">div> 
       <div>密码:<input type="password" name="password">div>
       <input type="submit" value="登录">              
    form>
  body>
html>

4.2 编写Controller的登录逻辑

package com.koncord.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {
     

	/**
	 * 测试
	 */
	@RequestMapping("/hello")
	@ResponseBody
	public String  hello(){
     
		System.out.println("UserController-hello");
		return "ok";
	}
	
	/**
	 * 测试 thymeleaf
	 */
	@RequestMapping("/testThymeleaf")
	public String  testThymeleaf(Model model){
     
		//将数据存入model
		model.addAttribute("name", "程序猿");
		//返回test.html
		return "test";
	}
	
	/**
	 * 进入添加页面
	 */
	@RequestMapping("/add")
	public String  add(){
     
		return "user/add";
	}
	
	/**
	 * 进入编辑页面
	 */
	@RequestMapping("/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){
     
		/**
		 * 使用Shiro编写认证操作
		 */
		//1.获取Subject
		Subject subject=SecurityUtils.getSubject();
		//2.封装用户数据
		UsernamePasswordToken token = new UsernamePasswordToken(userName,password);
		try {
     
			//3.执行登录方法
			subject.login(token);
			//只要没有异常,则登录成功;有异常则登录失败
			
			//登录成功,跳转test.html
			return "redirect:/testThymeleaf";//重定向请求
		} catch (UnknownAccountException e) {
     
			//登录失败
			model.addAttribute("msg", "用户不存在");
			return "login";//返回页面链接
		} catch (IncorrectCredentialsException e) {
     
			//登录失败
			model.addAttribute("msg", "密码错误");
			return "login";
		}
	}
}

测试登录认证,发现没有进入认证方法,原因是 配置 shiro内置过滤器 需要把 登录 过滤掉,修改添加如下:

filterMap.put("/login", "anon");

之后重新测试,发现可以正常进入认证逻辑。SpringBoot 与Shiro 整合- 用户认证与授权实战_第3张图片

4.3编写Realm的判断逻辑

package com.koncord.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * 自定义 Realm
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{
     

	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
		System.out.println("执行授权逻辑");
		return null;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
		System.out.println("执行认证逻辑");
		
		//假设数据库用户名和密码
		String userName="admin";
		String password="111111";
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		if(!userToken.getUsername().equals(userName)){
     
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:密码       第三个参数:realm的名字
		return new SimpleAuthenticationInfo("", password, "");
	}

}

测试结果:
1.输入错误的用户名密码:zs 111111
SpringBoot 与Shiro 整合- 用户认证与授权实战_第4张图片
点击登录,登录失败,提示信息如下:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第5张图片
2.输入正确的用户名密码 :admin 111111
SpringBoot 与Shiro 整合- 用户认证与授权实战_第6张图片
点击登录,登录成功,并跳转 test.html页面:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第7张图片

每执行一次 登录逻辑,都会执行一次认证逻辑

5.整合MyBatis实现登录

5.1 导入mybatis相关的依赖

pom.xml:

 
    
    <dependency>
      <groupId>org.mybatis.spring.bootgroupId>
      <artifactId>mybatis-spring-boot-starterartifactId>
      <version>1.3.0version>
    dependency>
    
	<dependency>
		<groupId>mysqlgroupId>
		<artifactId>mysql-connector-javaartifactId>
		<scope>runtimescope>
	dependency>
	
	<dependency>
		<groupId>com.alibabagroupId>
		<artifactId>druidartifactId>
		<version>1.1.9version>
	dependency>    

创建mysql数据库,新建一张简单的用户表:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第8张图片
插入一条数据:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第9张图片

5.2 配置application.properties

其位置:src/main/rescources/目录下

## 配置数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root

#数据库连接池 druid数据源
##使用连接池类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#指定实体类包名(别名包的扫描配置)
mybatis.type-aliases-package=com.koncord.model

5.3 编写 User实体类

package com.koncord.model;

public class User {
     

	private Integer id;//主键id
	private String name;//用户名
	private String password;//密码
	public Integer getId() {
     
		return id;
	}
	public void setId(Integer id) {
     
		this.id = id;
	}
	public String getName() {
     
		return name;
	}
	public void setName(String name) {
     
		this.name = name;
	}
	public String getPassword() {
     
		return password;
	}
	public void setPassword(String password) {
     
		this.password = password;
	}
	
	
}

5.4 编写 UserMapper接口

package com.koncord.mapper;

import com.koncord.model.User;

public interface UserMapper {
     

	/**
	 * 根据用户名获取用户信息
	 */
	public User findUserByName(String name);
}

5.5 编写 UserMapper.xml映射文件

位置:与UserMapper.java放在一个目录下,且与UserMapper.java同名
在这里插入图片描述



<mapper namespace="com.koncord.mapper.UserMapper">
  
  
  <select id="findUserByName" parameterType="java.lang.String" resultType="User">
    select id,name,password
    from sys_user
    where name=#{name}
  select>
  
mapper>

5.6 编写业务接口和实现

接口:UserService

package com.koncord.service;

import com.koncord.model.User;

public interface UserService {
     

	/**
	 * 根据用户名获取用户信息
	 */
	public User findUserByName(String name);
}

实现类:UserServiceImpl

package com.koncord.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.koncord.mapper.UserMapper;
import com.koncord.model.User;
import com.koncord.service.UserService;

@Service
public class UserServiceImpl implements UserService{
     

	//注入Mapper接口
	@Autowired
	private UserMapper userMapper;
	
	/**
	 * 根据用户名获取用户信息
	 */	
	@Override
	public User findUserByName(String name) {
     
		return userMapper.findUserByName(name);
	}

}

5.7 添加@MapperScan注解

如何识别 UserMapper接口启动类中加上@MapperScan,@MapperScan需要写上要扫描的包 (mapper接口所在的包)

package com.koncord;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot 启动类
 * @author Administrator
 *
 */
@SpringBootApplication
@MapperScan("com.koncord.mapper")
public class Application {
     

	public static void main(String[] args){
     
		SpringApplication.run(Application.class, args);
	}

}

5.8 修改 UserRealm(自定义Realm类)

package com.koncord.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.koncord.model.User;
import com.koncord.service.UserService;

/**
 * 自定义 Realm
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{
     

	@Autowired
	private UserService userService;
	
	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
		System.out.println("执行授权逻辑");
		return null;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
		System.out.println("执行认证逻辑");
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据用户名获取用户信息
		User user=userService.findUserByName(userToken.getUsername());
				
		if(user == null){
     
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:数据库密码       第三个参数:realm的名字
		return new SimpleAuthenticationInfo("", user.getPassword(), "");
	}

}

5.8 测试

1.输入错误的用户名,提示用户不存在
2.输入正确的用户名和错误的密码,提示密码错误
3.输入正确的用户名和密码,登录成功并跳转test.html页面

SpringBoot 与Shiro 整合- 用户认证与授权实战_第10张图片

二、SpringBoot 与Shiro 整合实现用户授权

1.使用Shiro内置过滤器实现授权页面拦截(授权过滤器)

需求:用户添加只有权限达到了,才可以访问;即对用户添加做权限拦截

package com.koncord.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

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;

/**
 * Shiro配置类
 * @author Administrator
 *
 */
@Configuration
public class ShiroConfig {
     

	/**
	 * 3.创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
     
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截
		 *    常用的过滤器:
		 *       anon:无需认证(登录)可以访问
		 *       authc:必须认证才可以访问
		 *       user:如果使用rememberMe的功能可以直接访问
		 *       perms:该资源必须得到资源权限才可以访问
		 *       role:该资源必须得到角色权限才可以访问
		 */
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
//		filterMap.put("/add", "authc");
//		filterMap.put("/update", "authc");
		filterMap.put("/testThymeleaf", "anon");
		filterMap.put("/login", "anon");
		
		//授权过滤器
		//注意:当前授权拦截后,shiro会自动跳转到未授权页面
		filterMap.put("/add", "perms[user:add]");
		
		filterMap.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		//设置登录跳转链接
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 2.创建 DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("userRealm")UserRealm userRealm){
     
		DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
		//关联realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 1.创建Realm
	 */
	@Bean
	public UserRealm userRealm(){
     
		return new UserRealm();
	}
}

测试:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第11张图片
SpringBoot 与Shiro 整合- 用户认证与授权实战_第12张图片

输入账号admin,密码111111,成功登录,点击“用户添加”,页面报401错误,未授权。

需要设置 未授权的页面跳转链接:
1.ShiroConfig 类的 shiroFilterFactoryBean方法中添加如下代码

//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

完整ShiroConfig代码:

package com.koncord.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

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;

/**
 * Shiro配置类
 * @author Administrator
 *
 */
@Configuration
public class ShiroConfig {
     

	/**
	 * 3.创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
     
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截
		 *    常用的过滤器:
		 *       anon:无需认证(登录)可以访问
		 *       authc:必须认证才可以访问
		 *       user:如果使用rememberMe的功能可以直接访问
		 *       perms:该资源必须得到资源权限才可以访问
		 *       role:该资源必须得到角色权限才可以访问
		 */
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
//		filterMap.put("/add", "authc");
//		filterMap.put("/update", "authc");
		filterMap.put("/testThymeleaf", "anon");
		filterMap.put("/login", "anon");
		
		//授权过滤器
		//注意:当前授权拦截后,shiro会自动跳转到未授权页面
		filterMap.put("/add", "perms[user:add]");
		
		filterMap.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		//设置登录跳转链接
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		//设置未授权提示页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 2.创建 DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("userRealm")UserRealm userRealm){
     
		DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
		//关联realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 1.创建Realm
	 */
	@Bean
	public UserRealm userRealm(){
     
		return new UserRealm();
	}
}

2.在UserController类中添加 未授权页面 的访问链接

	/**
	 * 进入未授权页面
	 */
	@RequestMapping("/noAuth")
	public String  noAuth(){
     
		return "noAuth";
	}

再次重启服务,进行测试:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第13张图片测试成功!

2.完成Shiro的资源授权(授权字符串写死)

修改UserRealm中的doGetAuthorizationInfo方法:

package com.koncord.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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.springframework.beans.factory.annotation.Autowired;

import com.koncord.model.User;
import com.koncord.service.UserService;

/**
 * 自定义 Realm
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{
     

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

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
		System.out.println("执行认证逻辑");
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据用户名获取用户信息
		User user=userService.findUserByName(userToken.getUsername());
				
		if(user == null){
     
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:数据库密码       第三个参数:realm的名字
		return new SimpleAuthenticationInfo("", user.getPassword(), "");
	}

}

3.关联数据库,动态授权

上面2的资源授权是写死的,实际开发中是应该是变得更加灵活,这里进行改造程序,把它和数据库进行一个简单的连接。

3.1 数据库表简单处理:修改用户表,新增一个字段perms,来存储用户的资源权限

SpringBoot 与Shiro 整合- 用户认证与授权实战_第14张图片
然后,模拟用户表数据如下:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第15张图片

3.2 User实体修改

package com.koncord.model;

public class User {
     

	private Integer id;//主键id
	private String name;//用户名
	private String password;//密码
	private String perms;//权限
	
	public Integer getId() {
     
		return id;
	}
	public void setId(Integer id) {
     
		this.id = id;
	}
	public String getName() {
     
		return name;
	}
	public void setName(String name) {
     
		this.name = name;
	}
	public String getPassword() {
     
		return password;
	}
	public void setPassword(String password) {
     
		this.password = password;
	}
	public String getPerms() {
     
		return perms;
	}
	public void setPerms(String perms) {
     
		this.perms = perms;
	}
	
	
}

修改 UserMapper.xml中的查询参数:
SpringBoot 与Shiro 整合- 用户认证与授权实战_第16张图片

3.3 改造UserRealm类中的授权方法

获取当前用户信息,查询数据库获取对应的授权字符串,添加这个授权字符串到SimpleAuthorizationInfo 对象中。
代码如下:

package com.koncord.shiro;


import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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;

import com.koncord.model.User;
import com.koncord.service.UserService;

/**
 * 自定义 Realm
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{
     

	@Autowired
	private UserService userService;
	
	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
		System.out.println("执行授权逻辑");
		//给资源进行授权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//添加资源的授权字符串
		//info.addStringPermission("user:add");
		
		//导数据库查询当前登录用户的授权字符串
		//获取当前登录用户
		Subject subject=SecurityUtils.getSubject();
		User user = (User) subject.getPrincipal(); 
		
		User sbUser=userService.findUserByName(user.getName());
		
		info.addStringPermission(sbUser.getPerms());
		return info;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
		System.out.println("执行认证逻辑");
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据用户名获取用户信息
		User user=userService.findUserByName(userToken.getUsername());
				
		if(user == null){
     
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:数据库密码       第三个参数:realm的名字
		return new SimpleAuthenticationInfo(user, user.getPassword(), "");
	}

}

SpringBoot 与Shiro 整合- 用户认证与授权实战_第17张图片

3.4 测试

两个用户进行测试
1.用户admin登录,admin用户有 用户添加 权限,没有 用户编辑 权限;
2.用户zhangsan登录,zhangsan用户没有 用户添加 权限,有 用户编辑 权限。

三、thymeleaf和shiro标签整合使用

1.导入thymeleaf扩展坐标

    
    
    <dependency>
      <groupId>com.github.theborakompanionigroupId>
      <artifactId>thymeleaf-extras-shiroartifactId>
      <version>2.0.0version>
    dependency>

2.配置ShiroDialect

在ShiroConfig类里面添加 getShiroDialect方法:

	
	/**
	 * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
	 */
	@Bean 
	public ShiroDialect getShiroDialect(){
     
		return new ShiroDialect();
	}

3.在页面上使用shiro标签

需求:不同的用户,看到的页面内容不同。
这里需要用到shiro标签 shiro:hasPermission

修改test.html页面:


<html>
  <head>
    <title>测试thymeleaf的使用title>
	
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    
    

  head>
  
  <body>
    <h3 th:text="${name}">h3><br>
    <div shiro:hasPermission="user:add">
    	进入用户添加页面:<a href="add">用户添加a>
    div>
    <div shiro:hasPermission="user:update">
    	进入用户编辑页面:<a href="update">用户编辑a>
    div>
    <a href="toLogin">登录a>
  body>
html>

测试:SpringBoot 与Shiro 整合- 用户认证与授权实战_第18张图片

认证逻辑与授权逻辑 执行次数:


根据演示视频,可以看出:
1.每次登录时执行一次认证逻辑 和 两次授权逻辑(和 ShiroConfig中配置 授权过滤器 个数有关 )
2.访问 单个授权过滤器 配置的链接时,执行一次授权逻辑;
3.访问非授权过滤器配置链接时,不进入授权逻辑。

四、完整代码

点击此处下载

你可能感兴趣的:(SpringBoot,java)