千家信息网

SpringBoot怎么整合Shiro

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,今天小编给大家分享一下SpringBoot怎么整合Shiro的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面
千家信息网最后更新 2025年01月24日SpringBoot怎么整合Shiro

今天小编给大家分享一下SpringBoot怎么整合Shiro的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

SpringBoot整合Shiro思路分析

鉴权流程分析

我们将我们的SpringBoot应用整合shiro,主要目的就是让shiro帮我们处理认证和授权的相关内容。也就是说,我们需要让shiro接管我们SpringBoot应用的会话。让用户的每一次请求都经过shiro进行认证和授权。因此,我们需要将用户请求拦截下来转发给shiro处理,这个拦截器是shiro提供的,ShiroFilter

步骤

  1. 用户通过客户端(浏览器、手机App、小程序)发起请求

  2. ShiroFilter拦截请求并判断请求访问的资源是否为受保护资源:

    2.1 是,则执行步骤3

    2.2 不是,则直接放行

  3. 判断用户是否已通过认证:

    3.1 是 ,则执行步骤4

    3.2 否,将用户请求重定向到认证页面,让用户先认证

  4. 获取用户权限信息和访问资源所需要的权限信息进行比对:

    4.1 用户具备访问权限,则放行

    4.2 用户不具备权限,返回403的相应提示

数据库分析设计

我们通过MySQL保存我们的认证和权限的相关数据。采用用户-角色-权限模型实现动态管理用户权限信息。

我们将系统当中的菜单、按钮、后端接口都抽象成系统的资源数据。以下是数据库表的设计:

文末提供sql脚本的下载。

整合步骤

环境搭建

maven

创建一个SpringBoot的web应用,并引入如下依赖

    org.apache.shiro    shiro-spring-boot-web-starter    1.6.0

添加对用户、角色和资源的CRUD支持

这里代码就省略了,不影响理解,完整代码可以从文末提供的方式中下载。

配置Shiro

自定义Realm

/**自定义Realm,使用mysql数据源 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/6 9:09 */public class MySQLRealm extends AuthorizingRealm {    @Autowired    private IUserService userService;    @Autowired    private IRoleService roleService;    @Autowired    private IResourceService resourceService;    /**     * 授权     * @param principals     * @return     */    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        String username = (String) principals.getPrimaryPrincipal();        List roleList = roleService.findByUsername(username);        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();        for (Role role : roleList) {            authorizationInfo.addRole(role.getRoleName());        }        List roleIdList  = new ArrayList<>();        for (Role role : roleList) {            roleIdList.add(role.getRoleId());        }        List resourceList = resourceService.findByRoleIds(roleIdList);        for (Resource resource : resourceList) {            authorizationInfo.addStringPermission(resource.getResourcePermissionTag());        }        return authorizationInfo;    }    /**     * 认证     * @param token     * @return     * @throws AuthenticationException     */    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        if(token==null){            return null;        }        String principal = (String) token.getPrincipal();        User user = userService.findByUsername(principal);        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());        return simpleAuthenticationInfo;    }}

shiro中的Realm对象充当了认证、授权数信息的据源作用。关于更多自定义Realm的内容请参考我的另一篇文章《Shiro入门学习-使用自定义Realm完成认证|练气中期》 。

ShiroConfig

/**shiro配置类 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/6 9:11 */@Configurationpublic class ShiroConfig {    /**     * 创建ShiroFilter拦截器     * @return ShiroFilterFactoryBean     */    @Bean(name = "shiroFilterFactoryBean")    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();        shiroFilterFactoryBean.setSecurityManager(securityManager);        //配置不拦截路径和拦截路径,顺序不能反        HashMap map = new HashMap<>(5);        map.put("/authc/**","anon");        map.put("/login.html","anon");        map.put("/js/**","anon");        map.put("/css/**","anon");        map.put("/**","authc");        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);        //覆盖默认的登录url        shiroFilterFactoryBean.setLoginUrl("/authc/unauthc");        return shiroFilterFactoryBean;    }    @Bean    public Realm getRealm(){        //设置凭证匹配器,修改为hash凭证匹配器        HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();        //设置算法        myCredentialsMatcher.setHashAlgorithmName("md5");        //散列次数        myCredentialsMatcher.setHashIterations(512);        MySQLRealm realm = new MySQLRealm();        realm.setCredentialsMatcher(myCredentialsMatcher);        return realm;    }    /**     * 创建shiro web应用下的安全管理器     * @return DefaultWebSecurityManager     */    @Bean    public DefaultWebSecurityManager getSecurityManager(Realm realm){        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        securityManager.setRealm(realm);        SecurityUtils.setSecurityManager(securityManager);        return securityManager;    }}

在编写shiro配置类这一步,需要大家注意的是,因为我们使用的是md5+salt+hash加密我们的密码,因此要换掉默认的凭证匹配器CredentialsMatcher对象,对于这部分的内容请参考我的另一篇文章《shiro入门学习-使用MD5和salt进行加密|练气后期》 。

实现认证模块

VO层

/**认证请求参数 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/7 15:12 */@Datapublic class LoginVO implements Serializable {    private String username;    private String password;}

web层

/**认证模块 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/6 10:07 */@RestController@RequestMapping("/authc")public class AuthcController {    @Autowired    private AuthcService authcService;    @PostMapping("/login")    public boolean login(@RequestBody LoginVO loginVO){        return authcService.login(loginVO);    }    @GetMapping("/unauthc")    public String unauthc(){        return "请先登录";    }    }

service层

/** * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/7 15:15 */@Servicepublic class AuthcServiceImpl implements AuthcService {    @Override    public boolean login(LoginVO loginVO) throws AuthenticationException {        if (loginVO==null){            return false;        }        if (loginVO.getUsername()==null||"".equals(loginVO.getUsername())){            return false;        }        if (loginVO.getPassword() == null || "".equals(loginVO.getPassword())){            return false;        }        Subject subject = SecurityUtils.getSubject();        UsernamePasswordToken token = new UsernamePasswordToken(loginVO.getUsername(), loginVO.getPassword());        subject.login(token);        return true;    }}

实现产品模块

/**产品模块 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/6 10:14 */@RestController@RequestMapping("/product")public class ProductController {        @RequiresPermissions("product:get")    @GetMapping("/get/list")    public String getProductList() {        return "productList";    }        @RequiresPermissions("product:delete")    @GetMapping("/delete")    public String deleteProduct() {        return "删除产品数据";    }}

对于注解实现访问控制,shiro主要有两个注解:RequiresPermissions和RequiresRoles。均可以用在类和方法上。具体用在哪可以根据自己的系统权限划分粒度决定。

对于这两个注解,有两个参数:

  1. value :分别对应permission的权限字符串值和role的角色名称;

  2. logical:逻辑运算符。这是一个枚举类型,有ANDOR两个值。当使用AND时表示需要满足所有传入的value值,OR表示仅需满足一个value 即可。默认为AND

关于shiro权限(访问控制)的更多内容,可以阅读我的另一篇文章《shiro入门学习-授权(Authorization)|筑基初期》

简单测试

认证通过的情况

认证未通过的情况

获取产品信息

请求没有访问权限的资源

默认的消息提示可以换一下。

未经过认证直接访问受保护资源

以上就是"SpringBoot怎么整合Shiro"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

0