0
点赞
收藏
分享

微信扫一扫

SpringSecurity(力争好理解,最细)

一点读书 2022-03-18 阅读 61
spring

SpringSecurity(力争好理解,最细)

目前比较出名的安全框架有shiro和security,security出身名门,spring框架给予了很好的支持。

  • security的操作颗粒度更高,安全性也会相对更好些。
  • spring框架中security的使用相对于shiro更加便捷。

security中文文档,有兴趣可以看看官方文档

https://www.springcloud.cc/spring-security-zhcn.html

1,什么是安全管理

整个项目的开发过程中,后端程序员最核心的业务,就是给前端提供一系列的api接口。

  • 前端需要一张静态资源图片,给她一个图片URL地址
  • 前端需要播放一个视频,给她一个视频资源URL
  • 前端需要新建一个用户,给她一个URL
  • 不管前端需要什么,我们给到的都是一个冷冰冰的URl地址。

所以我们能不能这么理解,互联网项目的 访问安全,就是对基于对URL地址的安全管理

2,怎样实现安全管理

所谓的安全管理,通俗的讲,就是把资源给到我想给的人

怎样才能把资源给到我想给的人呢,我们需要构建一个安全模型。我们把我们拥有的视频,图片,以及可以提供的服务称之为资源,把需要来获取资源的对象称之为用户。

2.1资源对应关系表

我们在用所有的用户和资源之间建立一 一个对应关系表,在表中规定,A资源能被a,b,c用户访问,B资源能被a,e,g用户访问,有了这样的对应关系表,资源的访问安全也就实现了。

2.2资源权限

如果我们的用户众多,资源众多,那么对应关系表的建立会十分的复杂,这张表也会非常的庞大。为了简化这样表,我们提出做一个新的概念:资源权限——访问资源需要的权利

假设A,B 资源我们想给的用户都新注册的用户,我们创建一个”newUser“的权限,把A,B两个资源划分到”newUser“的权限下,给新注册的用户贴上”newUser“的权限。那么我们的资源对应关系表可以极大的简化。

2.3用户角色

我们的用户需要是多样的,一个用户需要访问很多的资源,那么我们就需要给这个用户一 一添加需要的权限。如果我们的用户太多,给每个用户都添加多个权限,这也是比较麻烦的事,也会给我们在数据库中存储用户信息增加一点麻烦。我们发现用户与用户之间是有很多相似处的,可以按相似度进行分群,比如管理员用户,新用户,老用户等等。我们把这个相似处,抽象出来便有了角色。

3 security的安全管理流程

  1. 给资源添加访问限制权限

    • 资源是以URL做为访路径的
    • 给资源添加限制权限,也就是给路径添加限制规则
    • 资源的限制权限,可以值指定权限,也可以是不要权限(全体用户)
  2. 获取用户信息

    • 用户在提交登录后,security从数据库或者内存中获取用户信息,判断用户信息是否有一致
    • 用户信息一致,用户状态转为登录
    • 获取用户权限或角色信息,给用户添加对象权限
  3. 判断当前用户是否拥有权限

    • 用户在请求需要权限的资源时,需要判断用户是否有对应权限。
    • 如果当前用户未登录,跳转到登录,获取用户信息

4 项目实战

1.创建项目

  • 创建一个springboo项目
  • thymeleaf做页面模板引擎
  • MySql做数据库
  • mybatis做数据库管理
  1. 通过spring initial构建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfEvQGQN-1647606217093)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318170444133.png)]

2.在pom.xml文件中添加mybatis依赖

<!--  springboot 没给Mybatis做启动器 添加mybatis的启动器依赖-->
  <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.2.2</version>
  </dependency>
  <!--mysql连接-->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
  </dependency>
  

3,添加资源文件,这里以HTML做资源文件,在resources/templates文件下添加

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isFT4Vij-1647606217095)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171200802.png)]

4,在数据库中新建user表,并插入数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lf3nPenr-1647606217095)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171853076.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a0gj9i7U-1647606217096)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318171914082.png)]

5.创建UesrInfo类

package com.jade.entity;

import lombok.Data;
@Data
public class UserInfo {
    public Long id;
    public String username;
    public String password;
    public String role;
    public String power;
}

6,创建UserInfoRepository接口类,做mybatis的代理调用对象

package com.jade.Repository;

import com.jade.entity.UserInfo;
import org.springframework.stereotype.Repository;

@Repository
public interface UserInfoRepository {
    public UserInfo findByUsername(String username);
}

7,新建UserInfomapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jade.Repository.UserInfoRepository">

   <select id="findByUsername" parameterType="java.lang.String" resultType="com.jade.entity.UserInfo">
       select * from user where username=#{value}
   </select>

</mapper>

8.在启动类上添加导入mapper注解

@SpringBootApplication

@MapperScan("com.jade.Repository")

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

9.写application配置文件,我喜欢用yml格式,把application.properties修改application.yml

server:
  port: 8080
spring:
 # 配置视图模板配置器
  thymeleaf:
    mode: HTML
    prefix: classpath:/templates/
    suffix: .html
  #数据源  
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123123
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&amp
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
   mapper-locations: classpath:/mappers/*.xml  

10配置页面访问路径,新建MvcConfig类实现WebMvcConfigurer

package com.jade.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //配置页面访问路径,或者也可以写controller
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/login_on").setViewName("login");
        registry.addViewController("/main").setViewName("main");
        registry.addViewController("/user").setViewName("user");
    }
    
    //如果有静态资源
/*    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
       
    }*/
    
}

这时候启动项目,访问项目是不能访问的,security导入包后,就自动配置了一个用户,这是访问会出现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpVyaIeo-1647606217097)(C:\Users\zk\AppData\Roaming\Typora\typora-user-images\image-20220318181847973.png)]

开始配置security

11.创建MyUserDetails类 实现UserDetailsService,来完成用户认证

package com.jade.config;

import com.jade.Repository.UserInfoRepository;
import com.jade.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class MyUserDetails implements UserDetailsService {
    //导入userInfo对象的数据库代理对象
    @Autowired
    private UserInfoRepository userInfoRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //获取数据库中用户数据
        UserInfo userInfo = userInfoRepository.findByUsername(username);
        
        if (userInfo==null){
            throw  new UsernameNotFoundException("用户名不存在");
        }
        //给对象赋权限,如果需要创建角色,可以新建角色表,在这里查询角色权限,再将权限全部添加
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_"+userInfo.role));
                                                   //"ROLE_"一定要加
        return new User(username,
                       userInfo.password,
                      authorities
                  );
    }
}

12 创建MyWebSecurityConfig类,继承 WebSecurityConfigurerAdapter接口

配置访问资源权限,用户认证方式,登录方式

package com.jade.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.password.PasswordEncoder;

@EnableWebSecurity  //启用该配置类
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法上加权限
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetails myUserDetails;
    @Autowired
    private PasswordEncoder passwordEncoder;

    //资源限制权限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/","/index").permitAll()  //所有用户都已访问
                .antMatchers("/main").hasRole("main")   //main权限可以访问
                .antMatchers("/user").hasRole("user")   //user权限可以访问
               // .anyRequest().authenticated() //所有请求都需要验证,登录即可
                .and()
                .formLogin()   //开启使用表单登录
                .loginPage("/login_on") //自定义登录页面
                .failureForwardUrl("/login_on") //失败转发页面
                .successForwardUrl("/")      //成功转发页面
                .and()
                .logout()    //配置退出登录
                .and()
                .rememberMe()  //配置记住我
                .and()
                .csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
    }



    //用户认证管理
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      //在内存中添加用户信息,不在去数据库中读取,适合较小用户
      /*
          auth
                .inMemoryAuthentication() //在内存中添加用户信息,
                .withUser("supperMan").password(passwordEncoder.encode("123456")).roles("supper");

          */
      //从数据中读取用户
      auth.userDetailsService(myUserDetails).passwordEncoder(passwordEncoder);
    }
}

13 新建TestController类,测试在方法上加权限

package com.jade.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("test")
public class TestController {

    @RequestMapping("/user")
    @ResponseBody
    @PreAuthorize("hasAnyRole('user')") // 只能user角色才能访问该方法
    public String userTest(){

        return "你有User权限";
    }

    @RequestMapping("/main")
    @ResponseBody
    @PreAuthorize("hasAnyRole('main')") // 只能user角色才能访问该方法
    public String mainTest(){

        return "你有main权限";
    }
}

14 设置html页面,测试交互逻辑,验证security

index.html

<body>
  This is index
<p>
    <a href="/main">main</a>
    <a href="/user">user</a>
</p>
<p>
    <a href="/logout">退出登录</a>
</p>
</body>

login.html

<form action="/login_on" method="post">
    <p> username : <input name="username" type="text"> </p>
    <p> password : <input name="password" type="text"> </p>
    <p> <input type="submit" value="登录"> </p>
</form>

mian.html

<body>
  this is main
</body>

user.html

<body>
   this is user
</body>

总结

security框架的使用并不麻烦,相对于shiro,可能不太还理解。

我们在使用时只需要记住几个核心类即可

  • UserDetailsService :从数据库中加载user信息

  • WebSecurityConfigurerAdapter :配置security的核心信息

  • @EnableWebSecurity :启用该security配置类

  • @EnableGlobalMethodSecurity(prePostEnabled = true) :启用方法基于注解

  • @PreAuthorize(“hasAnyRole(‘user’)”) :修饰方法需要权限

项目原码

gitee地址

https://gitee.com/magical-ke-ke/security-student.git

举报

相关推荐

0 条评论