Shiro学习二(基于springboot的授权操作)

4 篇文章 0 订阅
3 篇文章 0 订阅

基于springboot的授权操作
首先而言,要进行授权,就是要实现看人下菜碟这个操作,即赋予成员相应的角色,针对不同的角色,可以访问相应的页面,使用相应的资源,首先,在数据表设计时,每个成员具有一个role属性,指定这个成员所属的角色,角色在对应一个授权表,完成角色和权限的一对多关系,下面是数据表的结构
customer:
在这里插入图片描述下面是role,比较简单:
在这里插入图片描述接下来是权限表:
在这里插入图片描述在这个表中使用一个role_id完成和角色的对应,使得角色和权限完成一对多的映射
声明:以上数据表结构设计存在一些冗余和缺陷,然学习期间,一切从简
接下来我们需要进行授权,其实授权,说白了就是,资源的拦截和放行,在第一季已经做过,shiro总共有四种认证过滤器,第一季我们用了无需认证的anon和需要登陆才能进行操作的authc,下面可以使用perms进行授权,首先要进行资源的权限字符串的对应,这会精确到某个controller的某个方法,例如下面这个controller:

package com.example.lclepro.Controller;

import com.example.lclepro.Bean.LcleCustomer;
import com.example.lclepro.Bean.Msg;
import com.example.lclepro.Service.UserService;
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.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    UserService userService;

    @PostMapping("/login")
    public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password){
        ModelAndView mv = new ModelAndView();
        //获取到Subject对象
        Subject suject = SecurityUtils.getSubject();
        //将收集到的用户名和密码存储到token中
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //执行登入方法
        try {
            suject.login(token);
            mv.setViewName("pages/customer");
        }
        catch (UnknownAccountException e){
            //用户名不存在时抛出的异常
            mv.addObject("msg", "用户不存在,请找到合适的介绍人加入组织(๑•̀ㅂ•́)و✧");
            mv.setViewName("pages/index");
        }
        catch (IncorrectCredentialsException e){
            //用户名密码不匹配时抛出的异常
            mv.addObject("msg", "用户名与密码不匹配( ̄_, ̄ )");
            mv.setViewName("pages/index");
        }
        return mv;
    }

    //使用ajax进行数据交互的方法,需要加上@ResponseBody注解
    @GetMapping("/getAllCustomers")
    @ResponseBody
    public Msg getAllCustomers(){
        return userService.getAllCustomers();
    }
}

如果要对方法/getAllCustomers进行授权,就可以在自定义的Realm中完成授权方法如下

package com.example.lclepro.Config;


import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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.context.annotation.DependsOn;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {


    //实现拦截器
    //@Bean注解注释在方法上,将返回的实体类交给容器处理,id是方法名
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //设置拦截成功以后,访问的页面
        shiroFilterFactoryBean.setLoginUrl("/");
        //拦截器Map的创建
        Map<String, String> map = new HashMap<String, String>();
        //针对不同的url,使用不同的拦截器,放到map中,即可生效,完成拦截
        //拦截所有请求
        map.put("/*", "authc");
        //放行登陆请求
        map.put("/", "anon");
        //放行登录认证请求
        map.put("/user/login", "anon");
        map.put("/user/getAllCustomers", "perms[user:getAllCustomers]");
        //设置相关的map集合,完成拦截功能的收集
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //实现web安全管理器的加载
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("lcleRealm") LcleRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;

    }

    //实现Realm的加载
    @Bean
    public LcleRealm lcleRealm(){
        return new LcleRealm();
    }

    //要加入注解,指定方法的拦截,则需要加入以下的三个方法,开启注解模式
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * *
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * *
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

注意这一条代码:

map.put("/user/getAllCustomers", "perms[user:getAllCustomers]");

我们将,刚才那个方法,映射出了一个授权字符串即中括号里面的内容“user:getAllCustomers”,这时,用户进行访问,由于未进行授权,请求则会被拦截:
在这里插入图片描述注意看第二个401,这就是我们请求的方法,他由于没有被授权,请求失败了,那么,如何进行授权操作呢,我们不妨在自定义的Realm中加上一句授权代码:

package com.example.lclepro.Config;

import com.example.lclepro.Bean.LcleCustomer;
import com.example.lclepro.Service.UserService;
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;

public class LcleRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        Subject subject = SecurityUtils.getSubject();
        LcleCustomer customer = (LcleCustomer)subject.getPrincipal();
        System.out.println(customer.getUsername()+"开始执行授权逻辑!");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:getAllCustomers");
        return info;
    }

    //执行认证逻辑,执行login()方法以后,会进入到这里,进行用户名密码的比对
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //执行认证逻辑
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //获取密码
        String password = userService.isUser(token.getUsername());
        if(password==null){
            throw new UnknownAccountException();
        }
        else{
            LcleCustomer customer = userService.selectCustomerByName(token.getUsername());
            return new SimpleAuthenticationInfo(customer, password, "");
        }
    }
}

注意这个授权方法中的代码`

Subject subject = SecurityUtils.getSubject();
        LcleCustomer customer = (LcleCustomer)subject.getPrincipal();
        System.out.println(customer.getUsername()+"开始执行授权逻辑!");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:getAllCustomers");
        return info;

我们先从shiro中获取到,我们本次操作的对象,然后定义一个授权信息相关的String(多个授权也可以使用list),,如此我们就完成了简单的授权,重新启动页面,进行测试
在这里插入图片描述我们看到,原先的401已经消失,资源访问正常,但是这样做未免会很傻,因为一个正常的项目,硬编码进去如此多的映射url,未免有些难受(尽管用户的权限可以持久化到数据库),这时候就体现出了注解的重要性,能否通过注解的方式,对每个controller的方法进行标记相应的授权url呢,答案是肯定的,不过我们的shiroConfig类需要做出变动,用来开启注解:

package com.example.lclepro.Config;


import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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.context.annotation.DependsOn;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {


    //实现拦截器
    //@Bean注解注释在方法上,将返回的实体类交给容器处理,id是方法名
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //设置拦截成功以后,访问的页面
        shiroFilterFactoryBean.setLoginUrl("/");
        //拦截器Map的创建
        Map<String, String> map = new HashMap<String, String>();
        //针对不同的url,使用不同的拦截器,放到map中,即可生效,完成拦截
        //拦截所有请求
        map.put("/*", "authc");
        //放行登陆请求
        map.put("/", "anon");
        //放行登录认证请求
        map.put("/user/login", "anon");
        map.put("/user/getAllCustomers", "perms[user:getAllCustomers]");
        //设置相关的map集合,完成拦截功能的收集
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //实现web安全管理器的加载
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("lcleRealm") LcleRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;

    }

    //实现Realm的加载
    @Bean
    public LcleRealm lcleRealm(){
        return new LcleRealm();
    }

    //要加入注解,指定方法的拦截,则需要加入以下的三个方法,开启注解模式
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * *
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * *
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

和之前相比,我们在最后加了三个方法,用来开启注解,那么我们接下来可以在每个方法头上加上一个注解@RequiresPermissions,用来标记这个方法的授权字符串,下面是controller的代码
package com.example.lclepro.Controller;

import com.example.lclepro.Bean.LcleCustomer;
import com.example.lclepro.Bean.Msg;
import com.example.lclepro.Service.UserService;
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.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping(“user”)
public class UserController {

@Autowired
UserService userService;

@PostMapping("/login")
public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password){
    ModelAndView mv = new ModelAndView();
    //获取到Subject对象
    Subject suject = SecurityUtils.getSubject();
    //将收集到的用户名和密码存储到token中
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    //执行登入方法
    try {
        suject.login(token);
        mv.setViewName("pages/customer");
    }
    catch (UnknownAccountException e){
        //用户名不存在时抛出的异常
        mv.addObject("msg", "用户不存在,请找到合适的介绍人加入组织(๑•̀ㅂ•́)و✧");
        mv.setViewName("pages/index");
    }
    catch (IncorrectCredentialsException e){
        //用户名密码不匹配时抛出的异常
        mv.addObject("msg", "用户名与密码不匹配( ̄_, ̄ )");
        mv.setViewName("pages/index");
    }
    return mv;
}

//使用ajax进行数据交互的方法,需要加上@ResponseBody注解
@RequiresPermissions("user:getAllCustomers")
@GetMapping("/getAllCustomers")
@ResponseBody
public Msg getAllCustomers(){
    return userService.getAllCustomers();
}

}
至此,简单的授权已经完成,shiro的工作也基本完成,要想完成多个权限的操作,不妨将权限和角色进行映射,再将每个人赋予不同的角色,然后在自定义的Realm中使用Service方法,获取当前用户的授权字符串,逐个加入info中,完成授权

为之则易,不为则难

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值