栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringSecurity-Shiro对比学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringSecurity-Shiro对比学习

文章目录
  • 一、安全简介
  • 二、SpringSecurity
    • 2.1 项目搭建
      • 2.1.1 引入maven依赖
      • 2.1.2 静态资源准备
      • 2.1.3 权限配置
      • 2.1.4 业务编写
    • 2.2 知识点整理
      • 2.2.1 SpringSecurity
      • 2.2.2 流程总结
  • 三、Shiro
    • 3.1 Shiro架构设计
    • 3.2 整合SpringBoot设计思路
    • 3.3 项目搭建
      • 3.3.1 核心pom依赖
      • 3.3.2 Shiro 配置
      • 3.3.3 业务编写

一、安全简介

在 Web 开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。因此,从应用开发的第一天就应该把安全相关的因素考虑进来,并在整个应用的开发过程中。

市面上存在比较有名的:Shiro,Spring Security

这里需要阐述一下的是,每一个框架的出现都是为了解决某一问题而产生了,那么Spring Security框架的出现是为了解决什么问题呢?

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。

Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求

从官网的介绍中可以知道这是一个权限框架。想我们之前做项目是没有使用框架是怎么控制权限的?对于权限 一般会细分为功能权限,访问权限,和菜单权限。代码会写的非常的繁琐,冗余。

怎么解决之前写权限代码繁琐,冗余的问题,一些主流框架就应运而生而Spring Scecurity就是其中的一种。

Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

二、SpringSecurity 2.1 项目搭建 2.1.1 引入maven依赖

    org.thymeleaf
    thymeleaf-spring5
    3.0.11.RELEASE



    org.thymeleaf.extras
    thymeleaf-extras-java8time
    3.0.4.RELEASE




    org.springframework.boot
    spring-boot-starter-security




    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-test

2.1.2 静态资源准备
index.html



    
    
    首页
    
    
    












login.html



    
    
    登录
    
    




登录

注册


blog.yeyoo.com

Spring Security Study by yeyoo

level.html



    
    
    首页
    
    
    




Level-1-1

复制八份用来测试权限

2.1.3 权限配置

我们需要 首页所有人可以访问,功能页只有对应权限的人才能访问
测试用例
拥有v1的权限可以访问level1下的文件
拥有v2的权限可以访问level2下的文件
拥有v3的权限可以访问level3下的文件

SecurityConfig
package com.SpringSecurity.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
//授权配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//    @Autowired
//    DataSource dataSource;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人可以访问,功能页只有对应权限的人才能访问
        //请求授权的规则
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("v1")
                .antMatchers("/level2/**").hasRole("v2")
                .antMatchers("/level3/**").hasRole("v3");

        //没有权限默认会跳到登录页面,需要开启登录的页面
        http.formLogin();
        //防止网站攻击:get;post
        http.csrf().disable();//关闭csrf(跨站请求伪造)功能,登出失败可能产生的原因
        //开启注销功能
        http.logout().logoutSuccessUrl("/");
    }

    //认证配置
    //认证,springboot 2.1.x可以直接使用,其他版本会报错(或者采用下面的密码编码解决)
    //密码编码:PasswordEncoder
    //在spring security 5.0+新增了很多的加密方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //数据库认证
//        auth.jdbcAuthentication().dataSource(dataSource).withDefaultSchema()
//                .passwordEncoder(new BCryptPasswordEncoder())
//                .withUser(User.withUsername("user").password("password").roles("roles"));

        //内存认证
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("yeyoo").password(new BCryptPasswordEncoder().encode("123456")).roles("v1", "v2")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("v1", "v2","v3");
    }
}
2.1.4 业务编写
RouterController 
@Controller
public class RouterController {
    //使得访问/,/index,/index.html都能跳到主页
    @RequestMapping({"/","/index","/index.html"})
    public String index(){
        return "index";
    }
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }
    //实现对level的三个页面的跳转,下面也是如此
    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level1/"+id;
    }
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }
}
2.2 知识点整理 2.2.1 SpringSecurity

Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理

记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略

  • AuthenticationManagerBuilder:自定义认证策略

  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。

“认证”(Authentication)

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。

身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

“授权” (Authorization)

授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。

这个概念是通用的,而不是只在Spring Security 中存在。

2.2.2 流程总结
  1. 引入 Spring Security 模块

    org.springframework.boot
    spring-boot-starter-security

  1. 编写 Spring Security 配置类
    参考官网:https://spring.io/projects/spring-security

查看我们自己项目中的版本,找到对应的帮助文档:

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

  1. 编写基础配置类 SecurityConfig

@EnableWebSecurity // 开启WebSecurity模式

  1. 定制请求的授权规则
三、Shiro 3.1 Shiro架构设计


为了简化配置并启用灵活配置/可插性,Shiro的实现都是高度模块化设计——由于如此的模块化,SecurityManager实现(以及它的类层次结构)并没有做很多事情。相反,SecurityManager 实现主要是作为一个轻量级的“容器”组件,委托计划所有的行为到嵌套/包裹的组件。

Shiro 核心三大组件

  • Subject
  • SecurityManager
  • Realm

Subject

Subject实质上是一个当前执行用户的特定的安全“视图”。鉴于"User"一词通常意味着一个人,而一个Subject可以是一个人,但它还可以代表第三方服务,daemon account,cron job,或其他类似的任何东西——基本上是当前正与软件进行交互的任何东西。

所有Subject实例都被绑定到(且这是必须的)一个SecurityManager上。当你与一个Subject交互时,那些交互作用转化为与SecurityManager交互的特定subject的交互作用。

SecurityManager

SecurityManager是Shiro架构的心脏,并作为一种“保护伞”对象来协调内部的安全组件共同构成一个对象图。然而,一旦SecurityManager和它的内置对象图已经配置给一个应用程序,那么它单独留下来,且应用程序开发人员几乎使用他们所有的时间来处理Subject API。

重要的是要认识到,当你正与一个Subject进行交互时,实质上是幕后的 SecurityManager处理所有繁重的Subject安全操作。这反映在上面的基本流程图。

简单的来说SecurityManager就是控制 Subject可以访问哪些资源。

Realms

Realms担当Shiro和你的应用程序的安全数据之间的“桥梁”或“连接器”。当它实际上与安全相关的数据如用来执行身份验证(登录)及授权(访问控制)的用户帐户交互时,Shiro 从一个或多个为应用程序配置的Realm中寻找许多这样的东西。

在这个意义上说,Realm本质上是一个特定安全的DAO:它封装了数据源的连接详细信息,使Shiro所需的相关的数据可用。当配置Shiro时,你必须指定至少一个Realm用来进行身份验证和/或授权。SecurityManager可能配置多个Realms,但至少有一个是必须的。

Shiro提供了立即可用的Realms来连接一些安全数据源(即目录),如LDAP,关系数据库(JDBC),文本配置源,像 INI 及属性文件,以及更多。你可以插入你自己的Realm 实现来代表自定义的数据源,如果默认地Realm不符合你的需求。

像其他内置组件一样,Shiro SecurityManager控制 Realms是如何被用来获取安全和身份数据来代表 Subject 实例的。

3.2 整合SpringBoot设计思路

3.3 项目搭建 3.3.1 核心pom依赖


    org.apache.shiro
    shiro-spring
    1.4.2



    com.github.theborakompanioni
    thymeleaf-extras-shiro
    2.0.0

3.3.2 Shiro 配置

UserRealm

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private IUserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了 => 授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");
        Subject subject = SecurityUtils.getSubject();
        //拿到user
        User currentUser =(User)subject.getPrincipal();
        //设置权限 数据库拿
        info.addStringPermissions(Arrays.asList(currentUser.getPerms().split(",")));
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了 => 认证");
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //用户名,密码去数据库取
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper = wrapper.eq(User::getName, userToken.getUsername());
        User user = userService.getOne(wrapper);
        if (user == null) {  //没有这个人
            return null;  //其实就是抛出UnknownAccountException异常
        }
        ByteSource passwordSalt = ByteSource.Util.bytes(userToken.getUsername());//这里的参数要给个唯一的;
        //之后密码认证,shiro   它自己会做
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPwd(), passwordSalt,"");
        Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser", user);
        return info;
    }
}

ShiroConfig

  • 创建reaml 对象 需要自定义 第一步
@Bean(name = "userRealm")
public UserRealm userRealm() {
    return new UserRealm();
}
  • DefaultWebSecurityManner 第二步
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    //关联UserRealm
    manager.setRealm(userRealm);
    return manager;
}
  • ShiroFilterFactoryBean 第三步
    Shiro内置过滤器

       anon:无需认证就可访问
       authc: 必须认证才可访问
       user: 必须拥有记住我才可访问
       perms: 拥有对某个资源权限才能访问
       role:拥有某个角色权限才能访问
    
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //添加shiro内置过滤器
        //拦截
        HashMap map = new linkedHashMap<>();

        //授权
        map.put("/user/add","perms[user:add]");
        map.put("/user/update","perms[user:update]");
//        map.put("/user/logout","logout");
        map.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(map);
        //设置登录请求
        bean.setLoginUrl("/toLoginShiro");
        //设置未授权页面
        bean.setUnauthorizedUrl("/unauthorized");
        return bean;
    }
3.3.3 业务编写

用户登录页面-不同的用户对应不同的权限-不同的权限展示不同的按钮
未登录之前点击任何按钮调转登录
显示的按钮 若当前用户无权访问 跳转无权访问页面

如上图 路径跳转以及权限配置在 config中标明

@Controller
public class UserController {

    @RequestMapping("/shiroIndex")
    public String shiroIndex(Model model) {
        model.addAttribute("msg", "shiro test");
        return "shiro/shiroIndex";
    }

    @RequestMapping("/user/add")
    public String shiroAdd() {
        return "shiro/shiroAdd";
    }

    @RequestMapping("/user/update")
    public String shiroUpdate() {
        return "shiro/shiroUpdate";
    }

    @RequestMapping("/toLoginShiro")
    public String toLogin() {
        return "shiro/shiroLogin";
    }

    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //获取当前用户
        Subject currentSubject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //登录
        try {
            currentSubject.login(token);
            return "shiro/shiroIndex";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg", "用户名不存在");
            return "shiro/shiroLogin";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "密码错误");
            return "shiro/shiroLogin";
        }
    }

    @RequestMapping("/unauthorized")
    @ResponseBody
    public String unauthorized(){
        return "无权访问";
    }

    @RequestMapping("/user/logout")
    public String logout(Model model){

        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        model.addAttribute("msg","安全退出!");
        return "shiro/shiroIndex";
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/349217.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号