SpringBoot-Security 具体案例、 实现安全框架、权限控制、aop切入

SpringBoot-Security 具体案例、 实现安全框架、权限控制、aop切入

SpringBoot-Security介绍

Security 官方序言

​ 安全是一个不断变化的目标,追求一个全面的、系统范围的方法很重要。在安全领域,我们鼓励您采用“安全层”,这样每个层都可以尽可能地保证自身的安全性,并且连续的层提供额外的安全性。每一层的安全性越“严格”,您的应用程序就越健壮和安全。在底层,为了减少中间人攻击,你需要处理诸如传输安全和系统辨识等问题。接下来,您将通常使用防火墙,也许是通过 vpn 或 IP 安全性来确保只有经过授权的系统才能尝试连接。在公司环境中,您可以部署 DMZ 来将面向公共的服务器与后端数据库和应用程序服务器分开。您的操作系统也将发挥关键作用,解决诸如作为非特权用户运行进程和最大化文件系统安全性等问题。操作系统通常也会配置自己的防火墙。希望在某个地方,你可以尝试阻止针对系统的分布式拒绝服务攻击攻击和暴力破解。入侵预防系统安全协议对于监控和响应攻击也特别有用,这样的系统能够采取保护措施,比如实时阻止违规的 TCP/IP 地址。转移到较高的层,您的 Java 虚拟机有望被配置为最小化授予不同 Java 类型的权限,然后您的应用程序将添加自己的问题域特定的安全配置。Spring Security 使后一个领域——应用程序安全性——更加容易。

​ 人们使用 Spring Security 有很多原因,但是大多数人是在发现 Java EE 的 Servlet 规范或 EJB 规范的安全特性之后才开始使用这个项目的,这些特性缺乏典型的企业应用场景所需要的深度。虽然提到了这些标准,但重要的是要认识到它们在 WAR 或 EAR 级别上是不可移植的。因此,如果切换服务器环境,在新的目标环境中重新配置应用程序的安全性通常需要做大量工作。使用 Spring Security 克服了这些问题,并且还为您带来了许多其他有用的、可定制的安全特性。

​ 您可能知道应用程序安全性的两个主要方面是“身份验证”和“授权”(或“访问控制”)。这是 Spring Security 的两个主要目标。“身份验证”是建立一个主体的过程,这个主体就是他们声称的那个人(“主体”通常指一个用户、设备或者其他能够在你的应用程序中执行某个操作的系统)。“授权”是指决定是否允许主体在应用程序中执行操作的过程。为了达到需要作出授权决定的地步,认证过程已经确定了主体的身份。这些概念是常见的,而且根本不是 Spring Security 特有的。

(自己看的时候,蛮喜欢就加在这篇文章的开头拉。)

文章是我在security 5.0.5. RELEASE 版本文档里看到的 。

https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/

文章简介

本文讲的是Spring Boot集成Spring-Security 框架。内容不牵扯到过多具体原理讲解,只是做了简单概述。

适合于初学者,状态大概:对这个不是很懂,但是暂时需要在项目中使用Spring-Boot-Security。

案例里有具体的数据库角色表 、权限表、资源表 是可以切入到已有项目的。

而且是添加注解的方式。

流程、原理等等。我应该也会去写滴,相信我… 对这个东东流程、原理,还是非常好奇的。

当然看完就好奇的就马上去Debug吧 奥利给!!!

项目中有一行代码用到了 jdk 8 中的 Optional 我之前写了文章讲解

这正好是一次实用。 大家可以看一看。

https://blog.csdn.net/weixin_45821811/article/details/115656637

数据库、表

我之前已经写完表的文章。如下

https://blog.csdn.net/weixin_45821811/article/details/115737401

步骤

1、创建Spring-boot项目

​ 工程结构图

2、导入依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
    </dependencies>

3、yaml配置文件

server:
  port: 8787

spring:
  datasource: #自己的数据库名 自己的数据库密码  
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=false&characterEncoding=utf8&serverTimezone=GMT
    password: 123456
    username: root

4、config

  • WebConfig

    @Configuration  // 相当于springmvc 文件
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("redirect:/login");
        }
    }
    
    
  • WebSecurityConfig 安全配置

    @Configuration
    @EnableWebSecurity  // 开启WebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)  // 这个开启权限注解
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable(). //屏蔽csrf
                    authorizeRequests()
    //                .antMatchers("/r/r1").hasAnyAuthority("p1") // 这种是没有开启权限注解 没有添加权限注解的方法
    //                .antMatchers("/r/r2").hasAnyAuthority("p2")
    //                .antMatchers("/r/r3").hasAnyAuthority("p1","p2")
                    .antMatchers("/r/**").authenticated()
                    .anyRequest().permitAll()
                    .and()
                    .formLogin()
                    .permitAll()
                    .and()
                    .logout()
                    .logoutUrl("/logout") // 退出url
                    .logoutSuccessUrl("/logout-success");  // 自定义登录成功的配置 成功后 转向这个url
        }
    }
    
    

5、pojo层

  • ​ MyUser 用户

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)// 链式
    public class MyUser {
        private String id;
        private String username;
        private String password;
        private String fullname;
        private String mobile;
    }
    
    
  • PermissionDto 资源权限

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)// 链式
    public class PermissionDto {
        private String id;
        private String code;
        private String description;
        private String url;
    }
    

6、Dao层

@Mapper
public interface MyUserDao {

    @Select("select * from user_db where username=#{username}")
    Optional<MyUser> getUserByName(String username);

    // 这里是多表联查 查询当前角色拥有的权限  表的结构在我另一篇文章有说明
    @Select("SELECT * FROM t_permission WHERE id IN( SELECT permission_id FROM t_role_permission WHERE role_id IN(SELECT role_id FROM t_user_role WHERE user_id = #{userId} ))")
    List<PermissionDto> findPermissionsByUserId(String userId);
}

7、Service

@Configuration
public class SpringDataMyUserDetailsService implements UserDetailsService {

    @Autowired
    MyUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<MyUser> user = userDao.getUserByName(username);
        if (user.isPresent()) {// 判空操作  这个是jdk8的一个新特性是 在我一篇文章中有讲
            MyUser myUser = user.get(); // 取出操作

            List<PermissionDto> list = userDao.findPermissionsByUserId(myUser.getId());
            System.out.println("list==>" + list.toString());
            //list==>[PermissionDto(id=1, code=p1, description=测试资源1, url=/r/r1)]

            List<String> permissions = new ArrayList<>();
            list.forEach(p -> permissions.add(p.getCode()));
            System.out.println("permissions===>" + permissions.toString());
            //permissions===>[p1]
            // 这里之所以转成数组 是因为 构建 UserDetails 时
            //.authorities(perarray)  这个添加权限的地方是可以添加一个数组的  更加方便我们个性定
            //参数:权限–此用户的权限(即ROLE_USER,ROLE_ADMIN等)。 不能为空或包含空值
            //		public UserBuilder authorities(String... authorities) {
            //			return authorities(AuthorityUtils.createAuthorityList(authorities));
            //		}


            String[] perarray = new String[permissions.size()];

            permissions.toArray(perarray);
            //创建userDetails
            UserDetails userDetails =
                    User.withUsername(myUser.getUsername()).password( BCrypt.hashpw(myUser.getPassword(), BCrypt.gensalt()) ).authorities(perarray).build();
            return userDetails;
        }
        return null;
    }
}

8、Controller层

@RestController
public class LoginController {

    //后端这返回类型 大家都知道吧 我这里是写的文本  json 是application/json 这种 还有这请求格式 我大都是复制  就不是那种restful 风格拉
    // 大家使用 自行修改哦
    @RequestMapping(value = "/login-success", produces = "text/plain;charset=utf-8")
    public String loginSuccess(){
        return getUsername()+"登录成功";
    }

    @RequestMapping(value = "/logout-success", produces = "text/plain;charset=utf-8")
    public String logout(){
        return "退出成功";
    }


    @RequestMapping(value = "/r/r1", produces = "text/plain;charset=utf-8")
//    @PreAuthorize("isAnonymous()")// 匿名访问
    @PreAuthorize("hasAuthority('p1')" )
    public String r1(){
        return getUsername()+"资源1";
    }
    @RequestMapping(value = "/r/r2", produces = "text/plain;charset=utf-8")
    @PreAuthorize(" hasAuthority('p2')" )
    public String r2(){
        return getUsername()+"资源2";
    }

    @RequestMapping(value = "/r/r3", produces = "text/plain;charset=utf-8")
    @PreAuthorize("hasAuthority('p1') or hasAuthority('p2')" )
    public String r3(){
        return getUsername()+"资源3";
    }

    // 这个是只要认证通过都可以访问 不需要主体带有什么权限
    @RequestMapping(value = "/r/r4", produces = "text/plain;charset=utf-8")
    public String r4(){
        return getUsername()+"资源4";
    }

    private String getUsername(){
        String username=null;
        //当前通过的用户身份
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal==null){
            username="匿名";
        }
        if(principal instanceof UserDetails){
            UserDetails userDetails=  (UserDetails)principal;
            username = userDetails.getUsername();
        }else{
            username= principal.toString();
        }

        return username;
    }
}

9、启动类

@SpringBootApplication
@MapperScan("com.wyh.dao")
public class SecuritySpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecuritySpringbootApplication.class, args);
    }

}

接下来就是测试哩。还有我的自言自语…

测试

认证

认证成功

在这里插入图片描述

- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i9qDiAVh-1618638067735)(springboot-security.assets/image-20210417131631065.png)]

- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-io1vhndp-1618638067736)(springboot-security.assets/image-20210417131616568.png)]
认证错误
-

退出

授权

admin 有p1 权限 能够访问 /r/r1 资源

- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HZzduRJN-1618638067739)(springboot-security.assets/image-20210417131151013.png)]
-

admin 没有p2 权限 访问不了 /r/r2 资源

/r/r3 是有p1 权限或者 有p2权限都是能够访问的 但是没有的话是不能访问的

- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9xA9K9mI-1618638067741)(springboot-security.assets/image-20210417131352209.png)]

/r/r4 是只要身份认证通过就是能访问的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9lr1xT0-1618638067742)(springboot-security.assets/image-20210417131554312.png)]

自言自语

每天记录一下自己的生活 也开始习惯每天这里写写 那里写写的感觉。

写这些一开始是想着方便自己看 但是写着写着就开始想要人来关注来看。

比起一开始随随便便写 现在的话要好一些拉。

就突然想把自己的知道的东西都记录下来,可能是想让人知道吧。

需要学的东西多的多啊。

继续加油哦。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值