sringboot整合shiro实现前后端鉴权控制,标签注解速成(包含常见错误的出现,前后端注解标签详解应用)

搭建shiro环境

1:导入boot项目中要用到的shiro依赖

 <!--shiro部分-->
        <!--shiro核心源码-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>
        <!--开启后端shiro标签支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--html中使用shiro标签-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

2:编写shiro的配置类

作用:开启后端shiro注解(重要)

@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
                = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

作用:启用shiro thymeleaf标签支持(重要)

    /**
     * 启用shiro thymeleaf标签支持
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

作用:配置shiro的拦截器工厂bean(这里比较容易踩坑)

属性含义
user登录且访问对应的url有相应的权限角色才能访问
authc必须认证了才能访问(进行鉴权)
anon无需任何权限或者角色就可以访问
perms[]指定的权限都要有才能访问
roles[]指定的角色都要有才能访问
setUnauthorizedUrl登录了但是没有相应权限时的跳转页面
setLoginUrl设置没有登录时的默认跳转页面
setFilterChainDefinitionMap设置资源访问权限(过滤规则)
setFilters设置我们的自定义拦截器(应用于url)
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
          ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //这里一定要用LinkedHashMap避免设置的过滤规则乱序
        LinkedHashMap<String, String> chain = new LinkedHashMap<String, String>();
        //设置自定义拦截器
        LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
        filters.put("jwtFilter", new jwtFilter());
        //这些路径下的资源不需要权限
        chain.put("/login", "anon"); // 登录链接不拦截
        chain.put("/css/**", "anon");
        chain.put("/img/**", "anon");
        chain.put("/js/**", "anon");
        chain.put("/lib/**", "anon");
        chain.put("/needRole", "roles[zzh]");
        chain.put("/noNeedRole", "anon");
        chain.put("/unAuth", "anon");
        chain.put("/testCustomExpection", "anon");
        chain.put("/needPerms", "perms[zzhperms,zzhperms2]");
        chain.put("/noNeedPerms", "anon");
        //测试jwtFilter
        chain.put("/shiroCheck", "jwtFilter");
        //chain.put("/api/auth/**", "noSessionCreation,jwtFilter");
        //chain.put("/api/auth/**", "noSessionCreation");
        //其他路径需要权限,这个一定要写在最后,这样所有的过滤规则才会生效
        chain.put("/**", "authc");
        //设置资源访问权限
        bean.setFilterChainDefinitionMap(chain);
        bean.setSecurityManager(securityManager());
        //设置没有登录时的默认跳转页面
        bean.setLoginUrl("/login");
        //登录了但是没有相应权限时的跳转页面
        bean.setUnauthorizedUrl("/unAuth");
        bean.setFilters(filters);
        return bean;
    }

遇到的坑(设置的一些过滤规则不生效)

由于过滤链是从上往下顺序执行的,chain要用LinkedHashMap不要用hashMap,hashMap的存储是无序的可能chain.put("/**", “authc”);先遍历到了,其他的访问路径也就不需要访问拦截了

还没解决问题?

chain.put("/**", “authc”);这个记得一定要写在最后,这样所有的过滤规则才会生效

这些配置比较通用读者可以自行配置(比较机械的代码)

不做过多说明


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        // 注入安全管理器
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    /**
     * @param
     * @method 配置安全管理器
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(shiroCacheManager);
        securityManager.setRealm(loginRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * @param
     * @method 注入realm
     */
    @Bean
    public loginRealm loginRealm() {
        return new loginRealm();
    }

    /**
     * @param
     * @method DefaultWebSessionManager的配置
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        SimpleCookie cookie = new SimpleCookie("myZzhSessionId");
        sessionManager.setSessionIdCookie(cookie);
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    /**
     * @param
     * @method 这里配置shiro的会话底层的缓存管理器,底层用的是redis
     */
    @Bean
    public SessionDAO sessionDAO() {
        shiroSessionDao shiroSessionDao = new shiroSessionDao(shiroCacheManager);
        return shiroSessionDao;
    }

编写测试controller

标签属性解释
@RequiresRolesvalue = {“shiro”, “zzh”}, logical = Logical.OR权限有一个就能访问此接口
@RequiresPermissionsvalue = {“add”, “select”}, logical = Logical.AND权限都有才能访问此接口
@Controller
@RequestMapping("/testShiroAno")
public class testShiroAnoController {
    @RequiresPermissions("shiroAdd")
    @RequestMapping("/add")
    @RequiresRoles({"shiro"})
    public String add() {
        return "add";
    }

    @RequestMapping("/delete")
    @RequiresPermissions("shiroDelete")
    public String delete() {
        return "delete";
    }

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

    @RequiresRoles(value = {"shiro", "zzh"}, logical = Logical.OR)
    @RequestMapping("/select")
    @RequiresPermissions("shiroSelect")
    public String select() {
        return "select";
    }
}

编写测试loginRelam

当执行subject.login(token)的时候就会走我们Relam中的逻辑。这里我用的模拟数据,里面的逻辑如下。

用户拥有的权限拥有的角色
zzhzzhperms和zzhperms2zzh
shiroshiroAdd和shiroDelete和shiroAltershiro

如果参数二是loginUser那么shiro会自动把loginUser中的password与参数二中的password作比较(我们无需编写比较密码的逻辑)

SimpleAuthenticationInfo参数一参数二参数三
简单认证对象信息认证的对象数据库中的密码就是realm的名称
simpleAuthorizationInfo
简单授权对象信息,我们可以对其赋予权限
public class loginRealm extends AuthorizingRealm {
    private Logger log = LoggerFactory.getLogger(loginRealm.class);
    /**
     * @param
     * @return
     * @function 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("授权。。。。。");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        ArrayList<String> perms = new ArrayList<>();
        Object key = principals.getPrimaryPrincipal();
        loginUser loginUser = new loginUser();
        try {
            BeanUtils.copyProperties(loginUser, key);
        } catch (Exception e) {
        }
        //TODO这里是模拟数据库中的权限表的情况
        if (loginUser.getName().equals("shiro")) {
            perms.add("shiroAdd");
            perms.add("shiroDelete");
            perms.add("shiroAlter");
            simpleAuthorizationInfo.addRole("shiro");
            simpleAuthorizationInfo.addStringPermissions(perms);
        }
        if (loginUser.getName().equals("zzh")) {
            simpleAuthorizationInfo.addRole("zzh");
            perms.add("zzhperms");
            perms.add("zzhperms2");
            simpleAuthorizationInfo.addStringPermissions(perms);
        }
        log.info("授权完成。。。。。");
        return simpleAuthorizationInfo;
    }

    /**
     * @param
     * @return
     * @function 认证     doGetAuthenticationInfo
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("认证....");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 1.获取用户输入的用户名
        String username = token.getUsername();
        // 2.获取用户输入的密码
        String password = new String(token.getPassword());
        loginUser loginUser = new loginUser();
        loginUser.setName(username);
        loginUser.setPassword(password);
        SimpleAuthenticationInfo info =
                new SimpleAuthenticationInfo(loginUser, "666", getName());
        log.info("认证完成....");
        return info;
    }
}

遇到的坑(获取PrimaryPrincipal信息的时候无法进行相应类型的对象转换)

这里我们直接暴力点用BeanUtils.copyProperties(loginUser, key);直接进行俩个对象间的属性赋值即可

权限,标签,注解对照表

测试的时候大家可以对着我这张参照表来对应着来看哦,铁质门(我现在就用过这么些shiro的标签,以后如果遇到过新的标签注解,再来补上来吧)

前端控制标签作用
shiro:hasPermission有此权限时显示按钮
shiro:hasAllPermissions有全部权限时显示 按钮
shiro:hasAnyPermissions有以下的任何一个权限时显示按钮
shiro:principal property=“name”获取已经登录用户的name属性的值
data-shiro-principal property=“password”获取已经登录用户的password属性的值
shiro:lacksPermission与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过
shiro:lacksRole与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
shiro:user认证通过登录过的用户
shiro:guest没登录过的用户(访客)
后端控制注解可选参数解释
@RequiresRoles(value = {“shiro”, “zzh”}, logical = Logical.OR)value(权限)logical(规则)只有当前用户同时拥有shiro和zzh这俩个角色时才能访问这个接口
@RequiresPermissions(value = {“shiro”, “zzh”}, logical = Logical.OR)value(权限)logical(规则)只有当前用户同时拥有shiro和zzh这俩个权限时才能访问这个接口

开始准备测试

直接编写一个add.html(由于是boot项目相应的路由规则不再啰嗦)注意导入th shiro标签支持

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

dui
用shiro用户登录的效果图
在这里插入图片描述
用zzh用户登录的效果图(我这里是用了自定义异常,自定义异常传送门

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小咸鱼的技术窝

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

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

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

打赏作者

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

抵扣说明:

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

余额充值