shiro认证和授权

添加依赖

<!-- 导入thymeleaf依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- shiro与spring整合依赖 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
    <scope>compile</scope>
</dependency>

ShiroConfig类

指定哪些路径可以直接访问,哪些路径需要登录认证后才能访问

package cn.tedu.springbootshiro02.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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;

@Configuration
public class ShiroConfig {
    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

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

        /**设置是controller中的那些路径需要登录认证,哪些不需要登录认证*/
        filterMap.put("/index", "anon");//这个路径放行
        filterMap.put("/login", "anon");//这个路径放行
        filterMap.put("/add", "authc");//授权的页面必须单独拎出来,设置需要登录验证,否则授权无效
        filterMap.put("/update", "authc");//授权的页面必须单独拎出来,设置需要登录验证,否则授权无效
        filterMap.put("/**", "authc");//其他页面需要登录才能访问
        //设置需要登录认证才能访问的页面,但未登录直接访问时的重定向请求
        shiroFilterFactoryBean.setLoginUrl("/toLogin");//对应的controller中有这个 @RequestMapping("/toLogin")

        /**设置授权拦截,访问controller中的/add路径,需要用户添加的权限,访问/update路径,需要用户更新的权限;
        授权的具体逻辑在UserRealm类的doGetAuthorizationInfo方法中*/
        filterMap.put("/add", "perms[add]");//[]中的字符串要跟数据库中perms字段存的字符串保持一致
        filterMap.put("/update", "perms[update]");//[]中的字符串要跟数据库中perms字段存的字符串保持一致
        //设置未授权重定向请求
        shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    /**
     * 创建Realm
     */
    @Bean(name="userRealm")
    public UserRealm getRealm(){
        return new UserRealm();
    }

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

}

UserRealm类

doGetAuthenticationInfo方法是具体的执行认证逻辑(与数据库中的数据比对校验)

package cn.tedu.springbootshiro02.shiro;

import cn.tedu.springbootshiro02.entity.User;
import cn.tedu.springbootshiro02.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 UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        //给当前登录用户 资源授权,授权字符串要跟filterMap.put("/add", "perms[add]");中[]字符串一致
        SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();

//      获取到当前登录用户 subject
        Subject subject = SecurityUtils.getSubject();
//这个principal就是下面登录认证逻辑中return new SimpleAuthenticationInfo(user,user.getPassword(),"");第一个参数user
        User user = (User) subject.getPrincipal();
//      根据用户名查询数据库
        User dbUser = userService.findByName(user.getName());
//      根据数据库查询到的权限信息,赋予该用户权限
        sai.addStringPermission(dbUser.getPerms());
        return sai;
    }
    /**
     * 执行认证(登录)逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证(登录)逻辑");

        //编写shiro判断逻辑,判断用户名和密码
        //1.判断用户名
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        User user = userService.findByName(token.getUsername());
        //判断用户输入的用户名跟数据库的用户名是否一致
        if(user==null){
            //用户名不存在
            return null;//shiro底层会抛出UnKnowAccountException
        }

        //2.判断密码
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

登录认证

controller写法

@RequestMapping("/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);//将token传给UserRealm类作为doGetAuthenticationInfo方法的参数进行登录验证
        //登录成功
        return "redirect:/index";//重定向到controller请求/index地址
    } catch (UnknownAccountException e) {//登录失败:用户名不存在
        model.addAttribute("msg", "用户名不存在");
        return "login";//转发到到templates目录下对应的html页面

    }catch (IncorrectCredentialsException e) {//登录失败:密码错误
        model.addAttribute("msg", "密码错误");
        return "login";
    }
}

UserRealm中认证逻辑

/**
 * 执行认证(登录)逻辑
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行认证(登录)逻辑");
    //编写shiro判断逻辑,判断用户名和密码
    //1.判断用户名
    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;//就是controller中 登录时传过来的token:subject.login(token); token中携带用户名和密码
    User user = userService.findByName(token.getUsername());
    //判断用户输入的用户名跟数据库的用户名是否一致
    if(user==null){
        //用户名不存在
        return null;//shiro底层会抛出UnKnowAccountException
    }

    //2.判断密码
    return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}

授权

1、在ShiroConfig类中指定哪些路径需要什么样的权限才能访问

filterMap.put("/add", "authc");//授权的页面必须单独拎出来,设置需要登录验证,否则授权无效
filterMap.put("/update", "authc");//授权的页面必须单独拎出来,设置需要登录验证,否则授权无效
filterMap.put("/add", "perms[add]");//[]中的字符串要跟数据库中perms字段存的字符串保持一致
filterMap.put("/update", "perms[update]");//[]中的字符串要跟数据库中perms字段存的字符串保持一致

2、在UserRealm类中的doGetAuthorizationInfo方法中赋予当前登录的用户什么样的权限

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        //给当前登录用户 资源授权,授权字符串要跟ShiroConfig类中filterMap.put("/add", "perms[add]");的[]字符串一致
        SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();

//      获取到当前登录用户 subject
        Subject subject = SecurityUtils.getSubject();
//这个principal就是下面登录认证逻辑中return new SimpleAuthenticationInfo(user,user.getPassword(),"");第一个参数user
        User user = (User) subject.getPrincipal();
//      根据用户名查询数据库
        User dbUser = userService.findByName(user.getName());
//      根据数据库查询到的权限信息,赋予该用户权限
        sai.addStringPermission(dbUser.getPerms());
        return sai;
    }

ShiroConfig类中的权限拦截规则也可以设置为基于注解的权限拦截

1、在shiroConfig类中添加

/**
 * 开启shiro注解支持,例如@RequiresRoles()和@RequiresPermissions()
 * shiro需要结合Spring的aop实现
 */
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
    DefaultAdvisorAutoProxyCreator dapc = new DefaultAdvisorAutoProxyCreator();
    dapc.setProxyTargetClass(true);
    return dapc;
}
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager){
    AuthorizationAttributeSourceAdvisor asa = new AuthorizationAttributeSourceAdvisor();
    asa.setSecurityManager(defaultWebSecurityManager);
    return asa;
}

2、在controller中,添加授权异常的拦截方法

/**
 自定义异常拦截,没有授权的异常,跳转到请求 @RequestMapping("/unAuth")
 当shiro出现权限验证失败后会抛出异常,因此必须写一个自定义的异常拦截,否则无法正常转发我们的没授权页面unAuth.html
 */
@ExceptionHandler(value = {UnauthorizedException.class})
public String unPerssion(Throwable throwable){
    return "unAuth";
}

3、在请求的资源上添加注解

/**
 *     @RequiresRoles(value = {"admin"})
 *     @RequiresPermissions(value = {"add","update"})
 *     是shiro的注解,用来访问该方法或者类需要什么角色
 */
@RequiresRoles(value = {"admin"})
@RequiresPermissions(value = {"add","update"})
@RequestMapping("/admin/hello")
@ResponseBody
public String hello(Model model){
    return "Admin下面的哈喽!";
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunny_yiyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值