springBoot框架下整合shiro

目录

一,前期准备

1,SpringBoot框架搭建

2.引入Thymeleaf页面模板

 

二、SpringBoot与Shiro整合

1.引入jar

2.定义Realm类

3.定义Shiro配置类

4.实现登录认证

4.1登录controller

4.2实现UserRealm内的doGetAuthenticationInfo(登录认证)方法

4.3ShiroConfig的配置

4.4登录认证流程

5.实现资源授权认证

5.1ShiroConfig的配置

5.2实现UserRealm内的doGetAuthorizationInfo(资源授权认证)方法

5.3资源授权认证流程

*注:shiro注解方式拦截问题

 

6.Thymeleaf与shiro标签整合使用

6.1前述

6.2导入jar

6.3在shiroconifg内配置ShiroDialect

6.4在页面内使用shiro标签

三、总结


 

  • 本文是在SpringBoot框架下整合shiro,

  • 利用Thymeleaf(SpringBoot推荐使用的页面模板,在HTML基础上添加了一些特定模板)

  • 进行shiro登录与权限相关实现

 

一,前期准备

1,SpringBoot框架搭建

这个就不多做阐述,可以利用idea的Spring Initializr来快速构建一个SpringBoot项目

2.引入Thymeleaf页面模板

注:我本次是结合了springBoot推荐的Thymeleaf模板引擎,进行前后端不分离的springBoot+shiro整合的,如果不使用Thymeleaf,则可忽略当前模块

<!--*注:thymeleaf3.0版本以前对标签语法要求较为严格,开始标签必须有对应的标签
如果希望页面语法不必过于严谨,可将thymeleaf升级为3.0及以上版本-->
<properties>
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
</properties>


<!--thymeleaf页面(springBoot框架推荐使用的页面模板,在HTML基础上添加了一些特定模板)-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>


src/main/resources目录下,创建固定目录名:templates

在templates下,创建所需的页面

如果位置写错,或目录名写错,在后面从controller跳转页面时,会报错

 

二、SpringBoot与Shiro整合

1.引入jar

<!--shrio-->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
</dependency>

2.定义Realm类


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;

/**
 * Created with IntelliJ IDEA
 * @Description: 自定义Realm
 **/
public class UserRealm extends AuthorizingRealm{
    
    /**
     * @Description: 执行授权逻辑
     **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权==========================");
        return null;
    }

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

自定义Realm类,

1.继承AuthorizingRealm

2.实现AuthorizingRealm的doGetAuthorizationInfo(授权)与doGetAuthenticationInfo(认证)两个方法

3.这个两个方法,在后面进行授权与认证相关逻辑时会实现具体方法

3.定义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;

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

/**
 * @Description: 自定义shiro配置
 **/
@Configuration
public class ShiroConfig {
    
    /**
     * @Description: 创建ShiroFilterFactoryBean
     **/
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//shiro内置的过滤器
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //添加Shiro内置过滤器
        /**
         *Shiro内置过滤器,可实现权限相关拦截器
         *      changy9ong过滤器:
         *          anon:无需认证(登录)可以访问
         *          authc:必须认证(登录)才可访问
         *          user:如果使用rememberMe的功能可直接访问
         *          perms:该资源必须得到资源授权才可访问
         *          role:该资源必须得到角色权限才可访问
         **/
        Map<String,String> filterMap = new LinkedHashMap<>();
        //认证拦截
        /*filterMap.put("/add","authc");
        filterMap.put("/update","authc");*///指定具体路径

        //无需认证
        filterMap.put("/test","anon");
        filterMap.put("/login","anon");

        //授权拦截
        //*注:"user:add"字符串可以自定义
        filterMap.put("/add","perms[user:add]");
        filterMap.put("/update","perms[user:update]");

        //认证拦截
        filterMap.put("/*","authc");//利用通配符,对指定目录下进行全部拦截


        shiroFilterFactoryBean.setLoginUrl("/tologin");//设置跳转的登录页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");//设置跳转的未授权页面
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        //*注意:使用了LinkedHashMap,进行了顺序存入,所以,filterMap存入是有先后顺序之分的!!!
        //我目前的路径顺序:排除路径—>授权路径—>拦截路径
        return shiroFilterFactoryBean;
    }

    /**
     * @Description: 创建DefaultWebSecurityManager
     **/
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //管理realm
        securityManager.setRealm(realm);
        return securityManager;

    }

    /**
     * @Description: 创建Realm
     **/
    @Bean(name = "userRealm")
    public UserRealm getRealm(){
        return new UserRealm();
    }
}

4.实现登录认证

4.1登录controller

@RequestMapping(value = "/login")
    public String login(String name,String password,Model model){
        /**
         *使用shiro编写认证操作
         **/
        //1.获取Subject
        Subject subject = SecurityUtils.getSubject();
        //2.封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(name,password);
        //3.执行登录方法
        try {
            subject.login(token);
            return "/test";
        }catch (UnknownAccountException e){//shiro封装的Exception
            model.addAttribute("msg","用户名不存在");
            return "/login";
        }catch (IncorrectCredentialsException e){//shiro封装的Exception
            model.addAttribute("msg","密码错误");
            return "/login";
        }
    }

4.2实现UserRealm内的doGetAuthenticationInfo(登录认证)方法

   @Autowired
    private EntityService entityService;

   /**
     * @Description: 执行认证逻辑操作
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证==========================");
        //实现shiro认证逻辑
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        Entity entity = entityService.gtEntityByName(token.getUsername());
        if (entity==null){
            return null;//shiro会爬出UnknownAccountException
        }
        //1).principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
        Object principal = entity;
        //2).Object credentials:密码
        Object credentials = entity.getPassword();
        //3).realmName:当前realm对象的name,调用父类的getName()方法即可
        String realmName = getName();
        return new SimpleAuthenticationInfo(principal,credentials,realmName);
    }

4.3ShiroConfig的配置

在getShiroFilterFactoryBean配置内,配置需要登录认证的路径、跳转登录页面的路径

在上面的ShiroConfig内已经配置,可参阅

4.4登录认证流程

输入账号密码

——>

Controller的Login接口接收账号密码

将账号密码传入shiro的UsernamePasswordToken

——>

UserRealm的doGetAuthenticationInfo()方法,authenticationToken接收账号密码信息

(1.)通过账号查询用户信息,未查询到,返回null,shiro内部会抛出UnknownAccountException异常

(2.)如查询到用户信息,无需判断密码,将查询出的用户信息内的密码返回,shiro会内部判断,如果不与传入的密码不同,则会抛出IncorrectCredentialsException异常

5.实现资源授权认证

5.1ShiroConfig的配置

在getShiroFilterFactoryBean配置内,配置需要授权的路径、跳转授权提示页面路径

在上面的ShiroConfig内已经配置,可参阅

5.2实现UserRealm内的doGetAuthorizationInfo(资源授权认证)方法

    @Autowired
    private EntityService entityService;

   /**
     * @Description: 执行授权逻辑
     **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权==========================");
        //资源授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Subject subject = SecurityUtils.getSubject();
        Entity entity = (Entity)subject.getPrincipal();
        Entity dbEntity = entityService.selectByPrimaryKey(entity.getId());

        info.addStringPermission(dbEntity.getPerms());
        return info;
    }

5.3资源授权认证流程

1.shiroconfig内配置需要授权的路径与自定义的字符

 //*注:"user:add"字符串可以自定义
 filterMap.put("/lvjia/carbodyad/add","perms[user:add]");
 filterMap.put("/lvjia/carbodyad/update","perms[user:update]");

在授权认证时,会匹配当前访问路径是否具有该自定义的权限名

2.UserRealm的doGetAuthorizationInfo()方法是进行授权认证的方法

获取到登录认证时传入的用户信息对象,

将用户资源权限信息交给SimpleAuthorizationInfo进行资源授权认证

3.如果资源授权认证通过,则跳转到资源路径

否则跳转到已配置的资源认证提醒路径

*注:shiro注解方式拦截问题

我们可以在controller方法上加

@RequiresPermissions("user:add")注解,进行权限验证拦截,

@RequiresRoles("admin")注解,进行角色拦截

在spring/springBoot项目内,只添加注解还不够,还需要下面两步配置(以SpringBoot为例,在shiroConfig内,Spring是在XML配置文件内):

1.开启AOP,对类的代理

    /**
     * @Description: 开启AOP
     **/
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);//对类的代理
        return advisorAutoProxyCreator;
    }

开启shiro注解支持

/**
     * @Description: 开启Shiro注解支持
     **/
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

 2.定义需要特殊处理的异常

配置这个原因:使用了注解进行权限/角色拦截认证后,如果无权限,应该会跳转到无权限的相应页面路径(在上面的shiroConfig内已经配置)。
但是会发现,并没有跳转到相应的页面/路径,而是直接抛出了无权限的异常。
这是因为shiro还没来得及捕获异常,就被spring大妈处理了....

所以我们有两种方式可以解决,

方法一、配置一个SimpleMappingExceptionResolver,来捕获特殊的异常,在里面进行异常处理

 /**
     * @Description: 定义需要特殊处理的异常,用类名或完全路径名作为Key,异常页面/接口地址为值
     *              解决Shiro注解拦截后直接抛异常,未来得及被shiro处理跳转指定路径的问题
     **/
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.AuthorizationException","redirect:/lvjia/carbodyad/noAuth");
        properties.setProperty("java.lang.Throwable","redirect:/lvjia/carbodyad/noAuth");//所有的异常都从属Throwable,配置了Throwable,所有的异常都会被捕获,处理
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }

方法二、使用@RestControllerAdvice / @ControllerAdvice注解定义一个异常捕获的controller,在controller内进行处理

//@RestControllerAdvice // 返回前端json
 @ControllerAdvice // 跳转到指定的页面资源
public class ExceptionController {

    // 捕捉 CustomRealm 抛出的异常 AuthorizationException:可以自定义异常类
    @ExceptionHandler(AuthorizationException.class)
    public String handleShiroException(AuthorizationException ex) {
        ex.printStackTrace();
        return "";
    }
}

@RestControllerAdvice 与 @ControllerAdvice的区别:

@ControllerAdvice的类可以拥有@ExceptionHandler, @InitBinder或 @ModelAttribute注解的方法,并且这些方法会被应用到控制器类层次的所有@RequestMapping方法上

@RestControllerAdvice 类似于 @RestController 与 @Controller的区别

 

6.Thymeleaf与shiro标签整合使用

6.1前述

shiro自带了一些页面标签,可以使得前端页面结合后台进行一些权限控制

本次使用了thymeleaf与shiro标签的整合

6.2导入jar

<!--thymeleaf对shiro标签扩展-->
<dependency>
   <groupId>com.github.theborakompanioni</groupId>
   <artifactId>thymeleaf-extras-shiro</artifactId>
   <version>2.0.0</version>
</dependency>

6.3在shiroconifg内配置ShiroDialect

   /**
     * @Description: 配置ShiroDialect,用于thymeleaf与shiro标签的配合使用
     **/
    @Bean
    public ShiroDialect getShiroDialectt(){
        return new ShiroDialect();
    }

6.4在页面内使用shiro标签

<h3>(当前用户拥有相应权限,即可看到此功能)</h3>
<div shiro:hasPermission="user:add">
    跳转用户添加<a href="add">添加</a>
</div>
<br/>
<div shiro:hasPermission="user:update">
    跳转用户修改<a href="update">修改</a>
</div>
<br/>
<br/>
<a href="tologin">登录</a>
<br/>

可以利用shiro:hasPermission标签对资源权限进行验证

如上代码:拥有新增权限即可看到并操作新增功能,拥有修改权限即可看到并操作修改功能

三、总结

至此,shiro与springBoot的整合与使用(登录认证、资源权限认证)就完成了

总结了一下,Shiro配合SpringBoot的登录与权限认证,主要涉及三部分

1.在Controller/service接口入口处接收用户输入的账号密码,开始调用shiro的登录验证方法,并将账号密码传入shiro的token内

2.在UserRealm的doGetAuthenticationInfo方法的入参接收到账号密码信息,并进行相应登录认证处理

3.而ShiroConfig内,最主要是shiro内部的filter过滤器的配置,需要配置相应登录拦截路径,授权拦截路径,忽略路径,跳转登录路径...

Shiro基本功能

Shiro的核心架构API

(从外部看)

 

(从内部看)

核心词概念

subject                   主体
principal                 身份信息
credential               凭证信息 
spring security        spring安全框架
Authentication        身份认证/登录
Authorization          授权
Cryptography          加密             
Concurrency           并发
realms                     领域
authorzing               授权

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值