关于shiro的和spring_boot集成shiro一些浅谈

发布于:2022-12-17 ⋅ 阅读:(175) ⋅ 点赞:(0)

关于shiro:

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

本教程只介绍基本的 Shiro 使用,不会过多分析源码等,重在使用。

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。这不就是我们想要的嘛,而且 Shiro 的 API 也是非常简单;其基本功能点如下图所示:

基本功能:

认证(Authentication):验证用户是否为合法用户

授权(Authorization):验证某个用户是否有权限访问某个特定的资源

session管理(Session Management):会话管理 管理特定用户的会话,在普通java项目中模拟httpsession的效果

加解密(Cryptography):加解密 使用加密算法确保数据安全,同时仍然易于使用

名词解释:************************

Realm : 域,提供数据的组件

  • getAuthencationInfo 入参 Token 出参 Info
  • Info 有 Principal + Credentials

AuthenticationToken:认证令牌,认证入参

AuthenticationInfo: 认证信息,用户信息

AuthorizationInfo: 授权信息

Principal: 经过认证的用户

Credentials: 凭证=密码

Shiro完整的异常体系参考:

  • RuntimeException
    • ShiroException
      • AuthenticationException
        • AccountException
          • DisabledAccountException
          • UnknownAccountException
        • CredentialsException
      • AuthorizationExeption
        • UnauthenticatedException

===========================================

认证(Authentication)

身份验证,即在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来表明他就是他本人,如提供身份证,用户名 / 密码来证明。

在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。

credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。

Subject 及 Realm,分别是主体及验证主体的数据源。

环境准备

1.首先准备环境依赖: 

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.4.1</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>1.5.6</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.0</version>
    </dependency>

添加 junitcommon-logging 及 shiro-core 依赖即可。

2、首先准备一些用户身份 / 凭据(shiro.ini

如上图roles即是角色而headmaster角色有两个权限,

users即是用户laoliu密码为wohenshuai,有两个角色为headmaster和teacher

3.写代码

 Subject也称为门面类,以此类登录(验证用户是否存在且合法),

此外可用subject.isAuthenticated();来验证用户是否是认证状态。

subject.hasRole("xxx角色");判断是否有某个角色。

subject.isPermitted("xxx权限");是否有权限。

关于Spring-boot集成shiro

shiro在spring中的运行原理:

请求先要经过shiro本身的shiroFilter中认证与授权然后传回spring的过滤器实现对于功能此处也解释了shiro的Web支持。

第一步:建项目spring-boot

第二步:导依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.1</version>
</dependency>

第三步:改配置

@Configuration//注意配置类
public class SecurityConfig {

    @Bean
    public Realm shiroRealm(){
        return new ShiroRealm();
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition sfcd = new DefaultShiroFilterChainDefinition();
        sfcd.addPathDefinition("/","anon");
        sfcd.addPathDefinition("/login","anon");
        sfcd.addPathDefinition("/login.html","anon");
        sfcd.addPathDefinition("/css/**","anon");
        sfcd.addPathDefinition("/js/**","anon");
        sfcd.addPathDefinition("/images/**","anon");
        sfcd.addPathDefinition("/fonts/**","anon");
        sfcd.addPathDefinition("/html/**","anon");
        //logout 登出
        sfcd.addPathDefinition("/logout","logout");
        //其他则需要认证
        sfcd.addPathDefinition("/**","user");

        return sfcd;
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
}

 上述标红方法可以解决关于重定义(302)无法访问跳转的问题!!!

第四步:写代码

@RestController
@Slf4j
public class LoginController {

    @PostMapping("/login")
    public Result login(String username, String password){
        log.info("{}{}",username,password);

        Subject subject = SecurityUtils.getSubject();
        subject.login(new UsernamePasswordToken(username,password));

        return Result.success();
    }

    @GetMapping("/pay")
    public String pay(){
        return "success";
    }

    @GetMapping("/destroyEarth")
    @RequiresPermissions("睡觉")
    public String destroyEarth(){
        return "success";
    }
}

注意:在执行登陆认证过后登录信息将会短暂保存,而在保存这个时间段我们称之为session,期间可以访问有权限的特殊资源,在logout后断开。

MybatisPlus实现ShiroRealm

package com.zjh.framework.shiro_boot_demo02.components;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zjh.framework.shiro_boot_demo02.dao.EduPermissionsMapper;
import com.zjh.framework.shiro_boot_demo02.dao.EduRoleMapper;
import com.zjh.framework.shiro_boot_demo02.dao.EduUserMapper;
import com.zjh.framework.shiro_boot_demo02.model.EduRole;
import com.zjh.framework.shiro_boot_demo02.model.EduUser;
import lombok.extern.slf4j.Slf4j;
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 javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class ShiroRealm extends AuthorizingRealm {
    @Resource
    private EduRoleMapper eduRoleMapper;

    @Resource
    private EduPermissionsMapper permMapper;

    @Resource
    private EduUserMapper userMapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        EduUser eduUser = (EduUser)principalCollection.getPrimaryPrincipal();

        //查找所有该用户登陆的角色
        List<EduRole> roles = eduRoleMapper.SelectByUserId(eduUser.getUserId());
        Set<String> collect = roles.stream().map(role -> role.getRoleName()).collect(Collectors.toSet());
        List<String> permissions= new ArrayList<>();
        if (roles.size()>0){
            permissions=permMapper.selectPermInRoleIds(roles);
        }
        SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
        authzInfo.setStringPermissions(new HashSet<>(permissions));
        authzInfo.setRoles(collect);
        return authzInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        Object username = token.getPrincipal();

        QueryWrapper<EduUser> qw = new QueryWrapper<>();
        qw.eq("user_name",username);
        EduUser eduUser = userMapper.selectOne(qw);

        return new SimpleAuthenticationInfo(eduUser,eduUser.getUserPwd(),getClass().getName());
    }
}

mapper:

<select id="selectPermInRoleIds" resultType="java.lang.String">
    select p.permissions_name
    from edu_role_perm rp join edu_permissions p on rp.permissions_id=p.permissions_id
    where rp.role_id in
    <foreach collection="roles" item="r" separator="," open="(" close=")">
        #{r.roleId}
    </foreach>
</select>

注意:1.导包时注意区分,容易混淆,导致无法运行(注意检查包javax!

2.yml(propertites)中注意配置mapper-locations

mybatis-plus:
  mapper-locations: classpath*:mappers/**/*.xml


网站公告

今日签到

点亮在社区的每一天
去签到