Shiro框架入门(代码实现)

0. 旨要

用于学习和记录,以方便后期开发和查找。第一次写文章,如有问题,感谢指出!!!

1. Shiro 简单介绍

Shiro是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。
三个核心组件:

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

2. 项目实战

环境:springboot + maven + mybatis
工具:IDEA
注:springboot 项目搭建就不多说。

2.1 依赖包

maven配置

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--    shiro 权限控制-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

2.2 验证授权配置

@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

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

        SysUserEntity principal = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        Set<String> roleCodeSet = new HashSet<>();//用户角色列表
        Set<String> permsSet = new HashSet<>();//用户权限列表
        SysUserRoleEntity sysUserRolesEntity = sysUserMapper.selectAllRoleMenusByUserId(principal.getId());
        List<SysRoleEntity> roles = sysUserRolesEntity.getSysRoleList();
        for (SysRoleEntity role : roles) {
            roleCodeSet.add(role.getCode());
            if (CollectionUtils.isNotEmpty(role.getSysMenuList())) {
                 role.getSysMenuList().stream().forEach(sysMenu -> permsList.add(sysMenu.getPerms()));
            }
        }
        for (String perm : permsList) {
            if (StringUtils.isNotBlank(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }

        SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
        authorInfo.setRoles(roleCodeSet);
        authorInfo.setStringPermissions(permsSet);
        return authorInfo;
    }

    /**
     * 执行认证逻辑,登录时调用
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;

        SysUserEntity user = sysUserMapper.selectByUsername(token.getUsername());
        if (user == null) {
            throw  new UnknownAccountException("账号不存在");
        }
        SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
        return authInfo;
    }
}

这里主要要说明下认证时的SimpleAuthenticationInfo对象,一共4个参数,按顺序说明:

  1. 第一个参数为当前用户信息,可以是用户的实体类,也可以是单单的一个用户账号,看个人需求,不过一般都是使用实体类;
  2. 第二个参数为数据库所存储的用户密码,subject.login时用于与token中的密码进行匹配,匹配不上会自动报异常(IncorrectCredentialsException);
  3. 第三个参数为盐,用于加密密码对比。常用的MD5、SHA256等等。注意,使用盐时数据库存储的密码也应是加密后的字符串(可以不设置);
  4. 第四个参数,当前 Realm 名称

2.3 配置

@Configuration
public class ShiroConfig {

    /**
     * 创建 Realm
     */
    @Bean
    public UserRealm myShiroRealm(CredentialsMatcher matcher) {
        UserRealm realm = new UserRealm();
        realm.setCredentialsMatcher(matcher);
        return realm;
    }

    /**
     * 创建安全管理器
     */
    @Bean
    public SecurityManager securityManager(UserRealm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 配置过滤器
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        Map<String, String> filterChainMap = new LinkedHashMap<>();
        filterChainMap.put("/webjars/**", "anon");
        filterChainMap.put("/logout", "logout");
        filterChainMap.put("/login", "anon");
        filterChainMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 密码加密验证
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256");
        matcher.setHashIterations(16);
        return matcher;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

2.3.1 ShiroFilterFactoryBean 说明

主要说明下loginUrlfilterChainMap

1) loginUrl

没有登录的用户请求需要登录的页面时自动跳转到登录页面,可配置也可不配置,但是不配置的话,会默认查找web下的login.jsp;

2) filterChainMap

Shiro内置过滤器,可以实现权限相关的拦截(url),依照顺序优先匹配,可以使用自定义的filter去覆盖。这里说的只是默认的拦截,没涉及到自定义filter。以下是一些常用的拦截配置说明:

  1. anon: 无需认证
  2. authc: 需要认证才可以访问
  3. user: 如果使用了rememberMe的功能可以直接访问
  4. perms: 该资源必须得到资源权限才可以访问
  5. roles: 该资源必须得到角色权限才可以访问

<font color=red>注:配置时注意别拦截自己的静态资源</font>

2.4 使用

以上1、2、3 步骤已经完成了Shiro的简单配置,也可以使用了

2.4.1 登录

   @PostMapping("/login")
   public String doLogin(String username, String password, Model model) {
       //1. 封装用户数据
       UsernamePasswordToken token = new UsernamePasswordToken(username, password);
       //2. 获取Subject
       Subject subject = SecurityUtils.getSubject();
       //3. 执行登录操作
       try {
           subject.login(token);
       } catch (IncorrectCredentialsException ice) {
           model.addAttribute("msg", "密码不正确");
           return "login";
       } catch (UnknownAccountException uae) {
           model.addAttribute("msg", "账号不存在");
           return "login";
       } catch (AuthenticationException ae) {
           model.addAttribute("msg", "状态不正常");
           return "login";
       }
       model.addAttribute("username", username);
       return "index";
   }

2.4.2 控制器访问权限控制

    @RequestMapping("/info")
    @RequiresPermissions("sys:user:info")//需要在Shiro配置文件里配置AOP才能生效
    public R getUserInfo(Long userId){
        ...
        return R;
    }

2.4.3 Shiro获取用户主体

SecurityUtils.getSubject().getPrincipal();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352