SpringBoot与Shiro整合-权限管理实战实操

 

一、搭建SpringBoot开发环境

1. 安装好开发软件和Maven等


开发工具:Spring Tool Suite(TST) 

Maven :3.3.9

jdk:1.8

2. 创建springboot项目(此功能需要联网)

SpringBoot与Shiro整合-权限管理实战实操_第1张图片

SpringBoot与Shiro整合-权限管理实战实操_第2张图片

3. pom.xml文件配置



	4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.1.3.RELEASE
		 
	
	com.vae
	shiro
	0.0.1-SNAPSHOT
	springboot-shiro
	Demo project for Spring Boot

    
        UTF-8
        UTF-8
        1.8
    

	
		
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
		
        
            org.springframework.boot
            spring-boot-devtools
            true
        
        
		
			org.springframework.boot
			spring-boot-starter
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	


4. 编写测试Controller层

package com.vae.user.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
@RequestMapping
public class UserController {
	
	@RequestMapping("/hi")
    
	public String hi(){
		return "hi shiro";
	}
}

5. 启动spring boot启动类

package com.vae;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * springboot启动类
 * @author vae
 *
 */
@SpringBootApplication
public class SpringbootShiroApplication {

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

6. 浏览器访问http://localhost:8080/hi 测试

SpringBoot与Shiro整合-权限管理实战实操_第3张图片

二、使用Thymeleaf页面模板


1. pom.xml 文件中添加 thymeleaf依赖



    org.springframework.boot
    spring-boot-starter-thymeleaf

2. 在UserController中编写测试thymeleaf的方法

下面写了两种方式,第一种比较直观和优雅,第二种相对普遍且代码较少,且迎合从struts2

注意:第二种方式返回String类型时,controller层不可以使用@RestController ,@RestController 等价于 @Controller 加上 @ResponseBody, @ResponseBody表示该方法的返回不会被解析为跳转, 而是直接写入http响应正文。

    @RequestMapping("/index")
    public ModelAndView index() {
        ModelAndView view = new ModelAndView();
        // 设置跳转的视图 默认映射到 src/main/resources/templates/{viewName}.html
        view.setViewName("index");
        // 设置属性
        view.addObject("title", "我的templates页面");
        view.addObject("desc", "欢迎进入我的csdn博客");
        Author author = new Author();
        author.setAge(18);
        author.setEmail("[email protected]");
        author.setName("vae");
        view.addObject("author", author);
        return view;
    }

	@RequestMapping("/index1")
    public String index1(HttpServletRequest request) {
        // TODO 与上面的写法不同,但是结果一致。
        // 设置属性
        request.setAttribute("title", "我的templates页面");
        request.setAttribute("desc", "欢迎进入我的csdn博客");
        Author author = new Author();
        author.setAge(18);
        author.setEmail("[email protected]");
        author.setName("vae");
        request.setAttribute("author", author);
        // 返回的 index 默认映射到 src/main/resources/templates/xxxx.html
        return "index";
    }
    
    class Author {
        private int age;
        private String name;
        private String email;
	    // 省略 get set        
    }

 

3. 在resources/templates文件夹下新建index.html模板文件

可以看到 thymeleaf 是通过在标签中添加额外属性动态绑定数据的




    
    
    Title
    
    


    

Hello World

=====作者信息=====

4. 浏览器访问http://localhost:8080/index测试

 

SpringBoot与Shiro整合-权限管理实战实操_第4张图片

三、shiro认证-ShiroConfig配置类


1. shiro的核心API


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


2. Spring整合shiro


(1)添加shiro和spring整合依赖



    org.apache.shiro
    shiro-spring
    1.4.0
 


(2)自定义Realm类

package com.vae.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;

public class UserRealm extends AuthorizingRealm{

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

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

}


(3)编写shiro配置类(基本结构)

package com.vae.shiro;

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 {
	/**
     * 创建ShiroFilterFactoryBean
     */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
	ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
//      设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		return shiroFilterFactoryBean;
	}

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

	/**
     * 创建Realm
     */

	@Bean(name="userRealm")
	public UserRealm getRealm(){
		return new UserRealm();
	}
}


四、shiro认证-使用shiro过滤器实现认证资源拦截


1. 在templates下新建user/add.html和user/update.html

è¿éåå¾çæè¿°
2. 在UserController中编写访问这两个页面的方法

	@RequestMapping("/add")
	public String add() {
		return "user/add";
	}

	@RequestMapping("/update")
	public String update() {
		return "user/update";
	}

3. 在index.html页面中添加这两个页面的超链接

进入用户新增页面:用户新增
进入用户更新页面:用户更新

测试可访问。

4. 修改ShiroConfig类的getShiroFilterFactoryBean方法中添加拦截

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

        //添加shiro内置过滤器,实现权限相关的url拦截
        /**
         * 常见过滤器:
         * anon:无需认证(登录)可以访问
         * authc:必须认证才可以访问
         * user:如果使用Remember Me的功能,可以直接访问
         * perms:该资源必须得到资源权限才可以访问
         * role:该资源必须得到角色权限才可以访问
         */
        Map filterMap=new LinkedHashMap();
        filterMap.put("/add", "authc");
        filterMap.put("/update", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

拦截之后重启,再次访问发现:试图访问add或者update,会自动跳转到login.jsp页面 

Filter 解释
anon 无参,开放权限,可以理解为匿名用户或游客
authc 无参,需要认证
logout 无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url
authcBasic 无参,表示 httpBasic 认证
user 无参,表示必须存在用户,当登入操作时不做检查
ssl 无参,表示安全的URL请求,协议为 https
perms[user] 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过
roles[admin] 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过
rest[user] 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等
port[8081] 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数

       

5. 修改拦截后跳转的页面


(1)在templates下新增login.html页面 
(2)在ShiroConfig中shiroFilterFactoryBean方法中修改拦截后跳转的页面

//修改跳转的登录页面,不加此项就会跳转到login.jsp页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");

(3)在UserController中添加toLogin方法

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

测试,当没有权限跳转到该login.html页面

6. 用通配符拦截

filterMap.put("/index", "anon");
filterMap.put("/*", "authc");

五、shiro认证-实现用户登录功能


1. 修改完善之前的login.html页面





登录页面


    

登录页面

          
       
       
           
 

2. 在UserController中编写/login请求,编写登录的处理逻辑

 

@RequestMapping("/login")
    public String login(String username,String password,Model model) {
        /**
         * 使用shiro编写认证操作
         */
        //获取Subject
        Subject subject=SecurityUtils.getSubject();
        //封装用户数据
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //执行登录方法
        try {
            //只要执行login方法,就会去执行UserRealm中的认证逻辑
            subject.login(token);

            //如果没有异常,代表登录成功
            //跳转到textThymeleaf页面,代表主页
            return "redirect:/index";
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            //登录失败
            model.addAttribute("msg","用户名不存在");
            return "login";

        }catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

测试发现,并没有进入/login请求,是因为之前写的拦截器(/*)拦截了所有请求,再对/login请求放行,加入代码:

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


重启测试,发现执行了认证逻辑,返回了用户名不存在异常。

3. 在UserRealm中编写shiro认证逻辑

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
        System.out.println("执行认证逻辑");

        //先写模拟数据进行验证,下一步再连接数据库,假设数据库的用户名和密码如下
        String dbusername="vae";
        String dbpassword="123456";

        //编写shiro判断逻辑,判断用户名和密码

        //1. 判断用户名
        UsernamePasswordToken token=(UsernamePasswordToken) arg0;
        if (!token.getUsername().equals(dbusername)) {
            //用户名不存在
            return null;//shiro底层会抛出UnknownAccountException
        }
        //2. 判断密码
        return new SimpleAuthenticationInfo("",dbpassword,"");//参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断 

    }



4. 启动程序,进行测试


六、shiro认证-整合MyBatis完善用户登录

SpringBoot与Shiro整合-权限管理实战实操_第5张图片


1. 导入Mybatis相关的依赖

        
		
			com.alibaba
			druid
			1.1.10
		

		
		
			mysql
			mysql-connector-java
		

		
		
			org.mybatis.spring.boot
			mybatis-spring-boot-starter
			1.1.1
		


2. 配置application.properties(src/main/resources下)


(1)创建数据库及用户表

mysql> CREATE DATABASE springboot_shiro;
Query OK, 1 row affected

mysql> use springboot_shiro;
Database changed

mysql> CREATE TABLE `user` (
  `id` int(11) NOT NULL COMMENT 'id',
  `username` varchar(20) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(20) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected


(2)配置application.properties

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_shiro?characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

# 连接池配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# mybatis 别名扫描
mybatis.type-aliases-package=com.fukaiit.domain

3. 编写User实体类

package com.vae.user.entity;

public class User {
	private Integer id;
	private String username;
	private String password;

    //省略get和set方法
}

4. 编写接口UserDao.java

package com.vae.user.dao;

import com.vae.user.entity.User;

public interface UserDao {
	/**
	 * 根据username查找用户信息
	 * @param username
	 * @return
	 */
	public User findByName(String username);
}

5. 编写UserMapper.xml映射文件





  
  
    
    
    
  
  
    id, username, password
  
  
  
    

6. 编写UserService.java业务接口

package com.vae.user.service;

import com.vae.user.entity.User;

public interface UserService {
	/**
	 * 根据username查找用户信息
	 * @param username
	 * @return
	 */
	public User findByUsername(String username);
}

7. 编写UserServiceImpl.java业务实现类

package com.vae.user.service.impl;

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

import com.vae.user.dao.UserDao;
import com.vae.user.entity.User;
import com.vae.user.service.UserService;

@Service
public class UserServiceImpl implements UserService{
	@Autowired
	private UserDao userDao;
	
	@Override
	public User findByUsername(String username) {
		return userDao.findByUsername(username);
	}

}

8. 在启动类添加@MapperScan注解,开启Mybatis的Mapper接口扫描

@MapperScan("com.vae.**.dao")

9. 修改UserRealm.java,调用刚编写的业务

@Autowired
	private UserService userService;
	/**
	 * 执行认证逻辑      
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");
		// 编写shiro判断逻辑,判断用户名和密码
		System.out.println(arg0.toString());
		// 1. 判断用户名
		UsernamePasswordToken token = (UsernamePasswordToken) arg0;
		User user = userService.findByUsername(token.getUsername());
		if (user==null) {
			//用户名不存在
			return null;//shiro底层会抛出UnknownAccountException
		}
		// 2. 判断密码
		// 参数1:需要返回给login方法的数据;参数2:数据库密码,shiro会自动判断 
		return new SimpleAuthenticationInfo("", user.getPassword(), "");
	}

10. 测试

SpringBoot与Shiro整合-权限管理实战实操_第6张图片

效果相同。

七、Shiro授权-使用Shiro过滤器实现授权页面拦截


1. 在ShiroConfig中添加过滤器

//授权过滤器:授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "perms[user:add]");
filterMap.put("/*", "authc");
Tips:注意要写在/*之前,否则不会拦截

2. 添加设置未授权页面


(1)ShiroConfig中

//修改自动跳转的未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");

(2)UserController中

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

(3)添加unAuth.html





未授权页面


    

抱歉!您无权限访问!

   

3. 访问 http://localhost:8080/add 测试

SpringBoot与Shiro整合-权限管理实战实操_第7张图片
登录认证之后,访问/add页面会提示未授权,而/update可以正常访问。

八、Shiro授权-编写资源授权逻辑


刚才打印的log日志中可以看到,只要访问了需要授权访问的资源,就会执行UserRealm中的doGetAuthenticationInfo()方法,在该方法中给资源进行授权。

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


测试查看效果:日志中可以看到执行了该授权逻辑,现在可以访问/add了

九、Shiro授权-关联数据库动态授权


1. 修改数据表


给user表添加perms字段,插入两个测试用户


2. 一系列小修改


(1)User.java:添加perms属性和getter/setter
(2)UserDao.java:

public User findById(Integer id);

(3)UserMapper.xml

  
  

(4)UserService.java

public User findById(Integer id);

(5)UserServiceImpl.java

    @Override
    public User findById(Integer id) {
        return userMapper.findById(id);
    }

(6)给/update添加资源拦截

filterMap.put("/update", "perms[user:update]");

3. 修改UserRealm中的doGetAuthorizationInfo方法

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        System.out.println("执行授权逻辑");
        
        //给资源进行授权
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        /*//添加资源的授权字符串
        info.addStringPermission("user:add");*/
        
        //获取当前用户
        Subject subject=SecurityUtils.getSubject();
        User user=(User)subject.getPrincipal();
        //到数据库查询当前登录用户的授权字符串
        User dbUser=userService.findById(user.getId());//通过当前登录用户id查找的数据库用户
        
        info.addStringPermission(dbUser.getPerms());        
        
        return info;
    }

将doGetAuthenticationInfo()方法的返回修改为 

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

因为User user=(User)subject.getPrincipal(); 所取得的当前登录用户就是从这里来的 

4. 登录不同权限用户进行测试


各自有了各自的权限。

十、ThymeLeaf和shiro标签整合使用


1. 导入thymeleaf对shiro的扩展坐标

        
        
            com.github.theborakompanioni
            thymeleaf-extras-shiro
            2.0.0
        

2. 配置ShiroDialect


ShiroConfig中

    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

3. 在页面上使用shiro标签

    进入用户新增页面:用户新增
    进入用户更新页面:用户更新


4. 运行测试


不同权限用户登录,只显示了他有权限看到的内容。

SpringBoot与Shiro整合-权限管理实战实操_第8张图片

十一.项目完整目录

SpringBoot与Shiro整合-权限管理实战实操_第9张图片

借鉴地址:SpringBoot与Shiro整合-权限管理实战视频笔记

重新整理了一边

 

你可能感兴趣的:(java技术)