springboot shiro mybatis mysql实现权限安全认证-kb88凯时官网登录

来自:
时间:2024-07-06
阅读:

shiro是apache 的一个强大且易用的java安全框架,执行身份验证、授权、密码学和会话管理。shiro 主要分为两个部分就是认证和授权两部分

一、介绍

  1. subject代表了当前用户的安全操作

  2. securitymanager:它是shiro框架的核心,典型的facade模式,shiro通过securitymanager来管理内部组件实例,并通过它来提供安全管理的各种服务。

  3. authenticator即认证器,对用户身份进行认证,authenticator是一个接口,shiro提供modularrealmauthenticator实现类,通过modularrealmauthenticator基本上可以满足大多数需求,也可以自定义认证器。

  4. authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

  5. realm充当了shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,shiro会从应用配置的realm中查找用户及其权限信息。

  6. sessionmanager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上。

shiro相关类介绍

  • (1)authentication 认证 —— 用户登录

  • (2)authorization 授权 —- 用户具有哪些权限

  • (3)cryptography 安全数据加密

  • (4)session management 会话管理

  • (5)web integration web系统集成

  • (6)interations 集成其它应用,spring、缓存框架

二、依赖引入

完整的pom文件如下:



	4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.4.1
		 
	
	com.gt.shiro
	com.sunyue.shiro
	1.0-snapshot
	jar
	
		1.8
		1.1.10
		1.2.10
		2.1.4
		2.0.4
	
	
		
			org.springframework.boot
			spring-boot-starter-web
			
			
				
					org.springframework.boot
					spring-boot-starter-tomcat
				
			
		
		
		
			org.springframework.boot
			spring-boot-starter-jetty
		
		
			org.mybatis.spring.boot
			mybatis-spring-boot-starter
			${mybatis.version}
		
		
			org.springframework.boot
			spring-boot-starter-thymeleaf
		
		
			com.github.theborakompanioni
			thymeleaf-extras-shiro
			2.0.0
		
		
		
			org.apache.shiro
			shiro-spring
			1.4.0
		
		
			com.alibaba
			druid-spring-boot-starter
			${druid.verzion}
		
		
		
			com.github.pagehelper
			pagehelper-spring-boot-starter
			${pagehelper.version}
		
		
			mysql
			mysql-connector-java
			runtime
		
		
			org.projectlombok
			lombok
			true
		
		
			org.springframework.boot
			spring-boot-starter-test
			test
		
	
	
		
			
			
				org.springframework.boot
				spring-boot-maven-plugin
				
					ainclass>com.gt.shiro.springshiroapplication
				
			
		
	

三、配置文件

application.yml配置文件:
# 开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
# 用非严格的 html
spring.thymeleaf.mode=html
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.servlet.content-type=text/html
spring.datasource.druid.url=jdbc:mysql://localhost:3306/shiro?useunicode=true&characterencoding=utf-8&usessl=false&servertimezone=utc
spring.datasource.druid.username=root
spring.datasource.druid.password=admin
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
#springbootjdbc导入包不和以前一样
spring.datasource.druid.driver-class-name= com.mysql.cj.jdbc.driver
mybatis.type-aliases-package=com.gt.shiro.entity
mybatis.mapper-locations=classpath:mapper/*.xml
#打印数据库的操作
logging.level.com.example.springsecurity.dao=debug
#redis缓存
### 配置redis
mybatis.configuration.cache-enabled=true
# redis数据库索引(默认为0)
spring.redis.database=0
# redis地址
spring.redis.host=...
# redis服务器连接端口
spring.redis.port=6379
# redis服务器连接密码(默认为空)
spring.redis.password=sunyue
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-idle=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000

shiro两个重要的配置类:

  • 1.userrealm

      package com.gt.shiro.config;
      import com.gt.shiro.entity.testuser;
      import com.gt.shiro.server.testuserserver;
      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;
      import java.util.arraylist;
      import java.util.hashset;
      import java.util.list;
      import java.util.set;
      public class userrealm extends authorizingrealm {
      	@autowired
      	private testuserserver testuserserver;
      	/**
      	 * 执行授权逻辑
      	 *
      	 * @param principalcollection
      	 * @return
      	 */
      	@override
      	protected authorizationinfo dogetauthorizationinfo(principalcollection principalcollection) {
      		system.out.println("执行授权逻辑");
      		/*获取当前登录的用户信息*/
      		subject subject = securityutils.getsubject();
      		testuser testuser = (testuser) subject.getprincipal();
      		//设置角色,多个角色
      		/*set rolesset = new hashset<>();
      		rolesset.add(testuser.getrole());*/
      		//simpleauthorizationinfo info = new simpleauthorizationinfo(rolesset);
      		//给资源进行授权
      		simpleauthorizationinfo info = new simpleauthorizationinfo();
      		/*可以在以下list加入多个权限*/
      		/*list roles = new arraylist<>();
      		roles.add(testuser.getperms());
      		info.addroles(roles);*/
      		//设置权限
      		info.addrole(testuser.getrole());
      		//需要判断权限是否为空值(null是没有地址,""是有地址但是里面的内容是空的)
      		if (testuser.getperms() != null && !testuser.getperms().equals("")) {
      			info.addstringpermission(testuser.getperms());
      		}
      		return info;
      	}
      	/**
      	 * 执行认证逻辑
      	 *
      	 * @param authenticationtoken
      	 * @return
      	 * @throws authenticationexception
      	 */
      	@override
      	protected authenticationinfo dogetauthenticationinfo(authenticationtoken authenticationtoken) throws authenticationexception {
      		system.out.println("执行认证逻辑");
      		/*获取令牌*/
      		usernamepasswordtoken passwordtoken = (usernamepasswordtoken) authenticationtoken;
      		//取出用户名并且判断用户名是否和数据库一致
      		testuser testuser = testuserserver.selectonebyname(passwordtoken.getusername());
      		if (testuser != null) {
      			//进行认证,将正确数据给shiro处理
      			//密码不用自己比对,authenticationinfo认证信息对象,一个接口,new他的实现类对象simpleauthenticationinfo
      			/*    第一个参数随便放,可以放user对象,程序可在任意位置获取 放入的对象
      			 * 第二个参数必须放密码,
      			 * 第三个参数放 当前realm的名字,因为可能有多个realm*/
      			//若密码不正确则返回incorrectcredentialsexception异常
      			return new simpleauthenticationinfo(testuser, testuser.getpassword(), this.getname());
      		}
      		//若用户名不存在则返回unknownaccountexception异常
      		return null;
      	}
      }
    
  • 2.shiroconfig

      package com.gt.shiro.config;
      import at.pollux.thymeleaf.shiro.dialect.shirodialect;
      import org.apache.shiro.spring.security.interceptor.authorizationattributesourceadvisor;
      import org.apache.shiro.spring.web.shirofilterfactorybean;
      import org.apache.shiro.mgt.securitymanager;
      import org.apache.shiro.web.mgt.defaultwebsecuritymanager;
      import org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator;
      import org.springframework.beans.factory.annotation.qualifier;
      import org.springframework.context.annotation.bean;
      import org.springframework.context.annotation.configuration;
      import org.springframework.web.servlet.handler.simplemappingexceptionresolver;
      import java.util.linkedhashmap;
      import java.util.map;
      import java.util.properties;
      @configuration
      public class shiroconfig {
      	@bean
      	public shirofilterfactorybean getshirofilterfactorybean(@qualifier("securitymanager") defaultwebsecuritymanager defaultwebsecuritymanager) {
      		shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();
      		//设置安全管理器
      		shirofilterfactorybean.setsecuritymanager(defaultwebsecuritymanager);
      		//添加一些shiro的内置过滤器
      		/**
      		 * shiro 的内置过滤器可以实现权限的相关拦截
      		 * 常用过滤器
      		 * 1.anon:无需认证
      		 * 2.authc:必须认证才能访问
      		 * 3.user:如果使用rememberme功能可以访问
      		 * 4.perms:对应权限才能访问
      		 * 5.role:对应角色才能访问
      		 */
      		//登录状态下才可以访问main页面,manage权限可访问manage页面,admin角色可访问admin页面
      		map filtermap = new linkedhashmap();
      		filtermap.put("/main", "authc");
      		filtermap.put("/manage", "perms[manage]");
      		filtermap.put("/admin", "roles[admin]");
      		shirofilterfactorybean.setfilterchaindefinitionmap(filtermap);
      		//未登录状态下访问将跳转至login页面
      		// 如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
      		shirofilterfactorybean.setlogin;
      		// 登录成功后要跳转的链接
      		shirofilterfactorybean.setsuccess;
      		//无授限状态下访问将请求unauthor
      		shirofilterfactorybean.setunauthorized;
      		return shirofilterfactorybean;
      	}
      	@bean(name = "securitymanager")
      	public defaultwebsecuritymanager getdefaultwebsecuritymanager(@qualifier("userrealm") userrealm userrealm) {
      		defaultwebsecuritymanager defaultwebsecuritymanager = new defaultwebsecuritymanager();
      		//defaultwebsecuritymanager需要关联一个realm
      		defaultwebsecuritymanager.setrealm(userrealm);
      		return defaultwebsecuritymanager;
      	}
      	/**
      	 * 创建realm
      	 */
      	@bean(name = "userrealm")
      	public userrealm getrealm() {
      		return new userrealm();
      	}
      	@bean
      	public shirodialect shirodialect() {
      		return new shirodialect();
      	}
      	/**
      	 * 开启shiro的注解(如@requiresroles,@requirespermissions)
      	 * 配置以下两个bean(defaultadvisorautoproxycreator和authorizationattributesourceadvisor)即可实现此功能
      	 *
      	 * @return
      	 */
      	@bean
      	public defaultadvisorautoproxycreator advisorautoproxycreator() {
      		defaultadvisorautoproxycreator advisorautoproxycreator = new defaultadvisorautoproxycreator();
      		advisorautoproxycreator.setproxytargetclass(true);
      		return advisorautoproxycreator;
      	}
      	/**
      	 * 开启 shiro 的@requirespermissions注解
      	 *
      	 * @param securitymanager
      	 * @return
      	 */
      	@bean
      	public authorizationattributesourceadvisor authorizationattributesourceadvisor(securitymanager securitymanager) {
      		authorizationattributesourceadvisor authorizationattributesourceadvisor = new authorizationattributesourceadvisor();
      		authorizationattributesourceadvisor.setsecuritymanager(securitymanager);
      		return authorizationattributesourceadvisor;
      	}
      	/**
      	 * shiro出现权限异常可通过此异常实现制定页面的跳转(或接口跳转)
      	 *
      	 * @return
      	 */
      	@bean
      	public simplemappingexceptionresolver simplemappingexceptionresolver() {
      		simplemappingexceptionresolver resolver = new simplemappingexceptionresolver();
      		properties properties = new properties();
      		/*未授权处理页*/
      		properties.setproperty("org.apache.shiro.authz.unauthorizedexception", "/error.html");
      		/*身份没有验证*/
      		properties.setproperty("org.apache.shiro.authz.unauthenticatedexception", "/error.html");
      		resolver.setexceptionmappings(properties);
      		return resolver;
      	}
      }
    

四、数据连接和业务逻辑

  • 1.实体类

      package com.gt.shiro.entity;
      import lombok.data;
      import lombok.experimental.accessors;
      import java.io.serializable;
      import java.util.date;
      @data
      @accessors(chain = true)
      public class testuser implements serializable {
      	private integer id;
      	private string username;
      	private string password;
      	/*权限*/
      	private string perms;
      	/*角色*/
      	private string role;
      	/*加盐密码*/
      	private string salt;
      }
    
  • 2.dao和mapper

      package com.gt.shiro.dao;
      import com.gt.shiro.entity.testuser;
      import org.apache.ibatis.annotations.mapper;
      import java.util.list;
      @mapper
      public interface testusermapper {
      	list findall();
      	testuser selectone(integer id);
      	testuser selectonebyname(string username);
      	void insert(testuser testuser);
      	void update(testuser testuser);
      	void delete(integer id);
      }
    



	
	
	
	
		insert into test_user (id,username,password,perms,role,salt) value (#{id},#{username},#{password},#{perms},#{role},#{salt})
	
	
		update test_user set username = #{username},password=#{password},perms=#{perms},role=#{role},salt=#{salt} where id = #{id}
	
	
		delete from test_user where id = #{id}
	

  • 3.业务层及其实现

      package com.gt.shiro.server;
      import com.gt.shiro.entity.testuser;
      import org.springframework.stereotype.service;
      import java.util.list;
      @service
      public interface testuserserver {
      	/*查询所有*/
      	list selectall();
      	/*查询一个用户*/
      	testuser selectbyone(integer id);
      	/*通过名字查询一个用户*/
      	testuser selectonebyname(string name);
      	/*增加一个用户*/
      	void insert(testuser testuser);
      	/*删除一个用户*/
      	void delete(integer id);
      	/*更新一个用户*/
      	void update(testuser testuser);
      }
    

package com.gt.shiro.server.serverimpl;
import com.gt.shiro.dao.testusermapper;
import com.gt.shiro.entity.testuser;
import org.apache.shiro.crypto.securerandomnumbergenerator;
import org.apache.shiro.crypto.hash.simplehash;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import com.sunyue.shiro.server.testuserserver;
import java.util.list;
@service
public class testuserserverimpl implements testuserserver {
	@autowired
	private testusermapper testusermapper;
	@override
	public list selectall() {
		return testusermapper.findall();
	}
	@override
	public testuser selectbyone(integer id) {
		return testusermapper.selectone(id);
	}
	@override
	public testuser selectonebyname(string name) {
		return testusermapper.selectonebyname(name);
	}
	@override
	public void insert(testuser testuser) {
		//加密写法
		string salt = new securerandomnumbergenerator().nextbytes().tostring();
		string password= new simplehash("md5",testuser.getpassword(),salt,2).tostring();
		testuser.setpassword(password);
		testuser.setsalt(salt);
		testusermapper.insert(testuser);
	}
	@override
	public void delete(integer id) {
		testusermapper.delete(id);
	}
	@override
	public void update(testuser testuser) {
		testusermapper.update(testuser);
	}
}
  • 4.控制层

      package com.gt.shiro.controller;
      import com.gt.shiro.entity.testuser;
      import com.gt.shiro.server.testuserserver;
      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.crypto.hash.simplehash;
      import org.apache.shiro.subject.subject;
      import org.springframework.beans.factory.annotation.autowired;
      import org.springframework.stereotype.controller;
      import org.springframework.ui.model;
      import org.springframework.web.bind.annotation.*;
      @controller
      public class indexcontroller {
      	@autowired
      	private testuserserver testuserserver;
      	@getmapping("/{url}")
      	public string redirect(@pathvariable("url") string url) {
      		return url;
      	}
      	@requestmapping(value = {"/", "/index"}, method = requestmethod.get)
      	private string index() {
      		return "index";
      	}
      	@postmapping("/login")
      	public string login(string username, string password, model model) {
      		subject subject = securityutils.getsubject();
      		testuser testuser = testuserserver.selectonebyname(username);
      		if (testuser != null) {
      			//根据salt值和用户输入的密码计算加密后的密码
      			string salt = testuser.getsalt();
      			password = new simplehash("md5", password, salt, 2).tostring();
      			system.out.println(password);
      		}
      		usernamepasswordtoken token = new usernamepasswordtoken(username, password);
      		//usernamepasswordtoken token = new usernamepasswordtoken(username, testuser.getpassword());(不加密写法)
      		try {
      			//将用户名和密码通过token传给shiro进行认证
      			subject.login(token);
      			testuser user = (testuser) subject.getprincipal();
      			subject.getsession().setattribute("testuser", user);
      			return "index";
      		} catch (unknownaccountexception e) {
      			e.printstacktrace();
      			model.addattribute("msg", "用户名不存在");
      			return "login";
      		} catch (incorrectcredentialsexception e) {
      			e.printstacktrace();
      			model.addattribute("msg", "密码有误");
      			return "login";
      		}
      	}
      	@responsebody
      	@getmapping("/unauthor")
      	public string unauthor() {
      		return "权限不足,无法访问";
      	}
      	@getmapping("/logout")
      	public string logout() {
      		subject subject = securityutils.getsubject();
      		subject.logout();
      		return "login";
      	}
      	@postmapping("/register")
      	public string register(testuser testuser, model model) {
      		string username = testuser.getusername();
      		string password = testuser.getpassword();
      		if (username ** null || username.equals("")) {
      			model.addattribute("msg", "用户名不能为空");
      			return "register";
      		} else if (password ** null || password.equals("")) {
      			model.addattribute("msg", "密码不能为空");
      			return "register";
      		} else if (testuserserver.selectonebyname(username) != null) {
      			model.addattribute("msg", "用户名已被占用");
      			return "register";
      		} else {
      			testuserserver.insert(testuser);
      			return "login";
      		}
      	}
      }
    
  • 5.前端页面

    • (1)index.html

        
        
        
        	
        	insert title here
        	
        
        
        
        main
         | manage
         | admin
        
    • (2)login.html

        
        
        
        	
        	insert title here
        	
        
        
        
      用户名:
      密码:
    • (3)register.html

        
        
        
        	
        	insert title here
        	
        
        
        
      用户名:
      密码:
    • (4)main.html

        
        
        
        	
        	insert title here
        	
        
        
        
        
        
      
    • (5)manage.html

        
        
        
        	
        	insert title here
        	
        
        
        
        
        
      
    • (6)admin.html

        
        
        
        	
        	insert title here
        	
        
        
        
        
        
      
  • 6.数据库文件

      /*
      navicat mysql data transfer
      source server         : sunyue
      source server version : 50724
      source host           : localhost:3306
      source database       : shiro
      target server type    : mysql
      target server version : 50724
      file encoding         : 65001
      date: 2021-01-11 22:00:47
      */
      set foreign_key_checks=0;
      -- ----------------------------
      -- table structure for test_user
      -- ----------------------------
      drop table if exists `test_user`;
      create table `test_user` (
        `id` int(11) not null auto_increment,
        `username` varchar(120) default null,
        `password` varchar(120) default null,
        `perms` varchar(120) default null,
        `role` varchar(120) default null,
        `salt` varchar(100) default null,
        primary key (`id`)
      ) engine=innodb auto_increment=8 default charset=utf8mb4;
      -- ----------------------------
      -- records of test_user
      -- ----------------------------
      insert into `test_user` values ('4', 'admin', '4867df2e009d0096c4cd8d9be8cc104c', 'manage', 'admin', 'gqr2m1n1o3nsljtozmitrq**');
      insert into `test_user` values ('5', 'user', '636502f40cf197dd2f4b19f56f475b24', '', '', 'kxw3hzifmgnluu8fmjmy7q**');
      insert into `test_user` values ('6', 'user1', '43f3133aa7e0ef9cf8373521dff8d8e8', 'manage', null, 'j8fn4hpauvnorluarl/spg**');
      insert into `test_user` values ('7', '1', '1', 'manage', null, null);
    
返回顶部
顶部
网站地图