0
点赞
收藏
分享

微信扫一扫

计算机是如何工作的??(多进程编程)

大雁f 2023-05-30 阅读 20

Java阶段三Day06

文章目录

同步请求和异步请求

  • 同步:指单线程依次做几件事
  • 异步:指多线程同时做几件事
  • 同步请求:指客户端浏览器只有一个主线程,主线程既要负责页面渲染 \ 监听用户的页面操作还需要负责发请求,当主线程发出请求时,会将页面中的内容清空,直到服务器响应了数据之后再将响应的数据展示出来,这个过程称为页面的整体刷新,同步请求无法实现页面的局部刷新
  • 异步请求:指客户端浏览器由主线程否则页面渲染和监听用户的页面的操作,由子线程负责发出请求,当子线程请求到数据之后,可以把得到的数据显示到原页面中,这就是页面的局部刷新

案例演示

创建SpringBoot工程

  • 工程导入依赖3个√MyBatis FrameworkMySQL DriverSpring Web

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

  • 在pom.xml添加以下依赖

    <!--引入Lombok依赖-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!--添加Knife4j依赖-->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
        <version>4.1.0</version>
    </dependency>
    
    <!--添加spring-validation依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
# 设置MyBatis框架的映射(Mapper)配置文件的位置
mybatis.mapper-locations=classpath:mappers/*.xml

logging.level.com.liner=debug

UserController

@RestController
@RequestMapping("/v1/users/")
public class UserController {
    @Autowired(required = false)
    UserMapper mapper;

    @RequestMapping("reg")
    public ResultVO reg(@RequestBody UserRegDTO userRegDTO){
        System.out.println("userRegDTO = " + userRegDTO);
        //判断用户名是否存在
        UserVO userVO = mapper.selectByUsername(userRegDTO.getUsername());
        if (userVO!=null){
            return new ResultVO(StatusCode.USERNAME_ERROR);
        }
        User user = new User();
        BeanUtils.copyProperties(userRegDTO,user);
        user.setCreated(new Date());

        mapper.insert(user);
        //当给客户端响应的是Java对象时SpringMVC框架会自动将Java对象转成
        //Json格式的字符串,然后将字符串响应给客户端.
        return new ResultVO(StatusCode.SUCCESS);
    }
}

UserMapper

public interface UserMapper {
    UserVO selectByUsername(String username);
}
<?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.liner.mapper.UserMapper">
    <select id="selectByUsername" resultType="com.liner.pojo.vo.UserVO">
        SELECT id, password, nickname
        FROM user
        WHERE username = #{username}
    </select>

</mapper>

静态页面

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册</title>
    </head>
    <body>
        <div>
            <input type="text" placeholder="用户名" v-model="user.username">
            <input type="text" placeholder="密码" v-model="user.password">
            <input type="text" placeholder="昵称" v-model="user.nickname">
            <input type="button" value="注册" @click="reg()">
        </div>
        <!--引入vue框架文件-->
        <script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>
        <!--引入axios框架文件-->
        <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
        <script>
            let v = new Vue({
                el:"body>div"data:{
                    user:{username:"",password:"",nickname:""}
                }methods:{
                    reg(){
                        //发出异步请求response代表服务器响应对象
                        //response.data得到响应的ResultVO对象  不是每一个请求都有data
                        axios.post("/v1/users/reg",v.user).then(function (response) {
                            //当接收到JSON格式的字符串的时候Axios框架会自动将JSON格式的字符串转成了JS的对象
                            if (response.data.code === 1){
                                alert("注册成功");
                                location.href="/";  //localhost:8080
                            }else{
                                alert("用户已存在!")

                            }
                        })
                    }
                }
            })
        </script>
    </body>
</html>

JSON

JSON:JavaScript Object Notation 是一种轻量级数据交换格式(也称为数据封装格式)

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

当服务器给客户端响应复杂数据时需要将数据封装到ava对象里面,但是Java对象不是跨平台的对象客户端是无法直接识别Java格式的对象的,这时候就需要将Java的对象先转成JSON格式的字符串,因为JSON字符串是跨平台的类型,客户端接收到JSON格式的字符串时,Axios框架会自动将其转成JS语言的对象

Spring Security

Spring Security是一个基于Spring框架的安全性认证和访问控制框架,主要用于保护Web应用程序。它提供了一组与安全相关的服务和类,使得开发者可以方便地为Web应用程序添加认证和访问控制功能。

主要作用:帮助我们进行登录的认证,和项目中涉及到权限时进行权限誓理操作

引入SpringSecurity框架

<!-- Spring Boot支持Spring Security的依赖项,用于处理认证与授权-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

对项目的影响

  • 所有的请求(包括根本不存在的)都必须登录,否则自动跳转到登录页面

  • 默认的用户名是user,密码是启用项目时在控制台提示的一串UUID值 ,如下图

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

  • 登录时,如果在打开登录页面后重启过服务器端,应该刷新登录页面,否则, 第1次输入并提交是无效的

  • 当登录成功后,会自动跳转到此前尝试访问的URL

  • 当登录成功后,可通过 /logout 退出登录

  • 默认不接受普通的POST请求,如果提交POST请求,会响应403(Forbidden)

关于SpringSecurity的配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {

    }
}

默认登录表单

@Override
protected void configure(HttpSecurity http) throws Exception {
    //如果调用以下方法,当需要访问通过认证的资源,但是未通过认证时,将自动跳转到登录页面
    //如果未调用以下方法,将响应403
    http.formLogin();
    // super.configure(http);//不要保留调用父类同名方法的代码,不要保留!不要保留!不要保留!

}

设置白名单

//设置白名单(不需要登录即可访问的资源)
String[] urls = {"/reg.html", "/login.html", "/v1/users/reg", "/v1/users/login"};
http.authorizeRequests()//对请求进行授权
    .mvcMatchers(urls)//匹配某些路径
    .permitAll() //直接许可,即不需要认证即可访问
    .anyRequest() //任意请求
    .authenticated();//要求通过认证的

模拟登录

  • 尝试登录用户名root,密码123456
  • 创建UserDetailsService接口的实现类,并实现loadUserByUsername方法
  • 此时进行登录时会自动调用此方法
  • 设置默认的密码编码为无编码
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    //配置密码加密的方式
    public PasswordEncoder passwordEncoder(){
        //NoOpPasswordEncoder.getInstance()获取一个无加密的实例
        return NoOpPasswordEncoder.getInstance();

    }
}
  • 登录成功需要响应一个UserDetail对象,如果响应的是一个null则会认为是用户名不存在
package com.liner.security;

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.Service;

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("username = " + username);
        //点击登录按钮

        UserDetails userDetail = User.builder()
            .username("root").password("123456")
            .disabled(false) //账号是否禁用
            .accountLocked(false)   //账号是否锁定
            .accountExpired(false)  //账号是否过期
            .credentialsExpired(false) //凭证是否过期
            .authorities("临时使用的权限") //权限
            .build();

        if (!username.equals("root")) {

            return null;    //代表用户名不存在
        }
        return userDetail;
    }
}

使用自己的页面进行登录

  • 需要在配置类中添加http.formLogin().loginPage("“/login.html");
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置自己的登录页面
        http.formLogin().loginPage("/login.html");
        //设置白名单
        String[] urls = {"/reg.html", "/login.html", "/v1/users/reg", "/v1/users/login"};
        http.authorizeHttpRequests()//对请求进行授权
            .mvcMatchers(urls)//匹配某些路径
            .permitAll() //直接许可,即不需要认证即可访问
            .anyRequest() //任意请求
            .authenticated();//要求通过认证的
    }
}
  • 使用自己的登录页面时,Security框架的登录认证流程不会自动启动,需要我们在UserController处理login请求时手动开启
  • 开启时需要在SpringSecurity框架的配置类中添加认证管理器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean  //添加此注解的目的是为了在Controller中自动装配
    @Override
    //添加认证管理器
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}
  • 添加后在自己的Controller里面添加以下代码才会触发UserDetailServicelmpl实现类
@Autowired
private AuthenticationManager authenticationManager;

@RequestMapping("login")
public ResultVO login(@RequestBody UserLoginDTO userLoginDTO) {

    //通过认证管理器启动Security的认证流程返回认证结果对象
    Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userLoginDTO.getUsername(), userLoginDTO.getPassword()));
    //将认证结果保存到Security上下文中让Security框架记住登录状态
    SecurityContextHolder.getContext().setAuthentication(authenticate);
    //代码执行到这里时代表登录成功!如果登录失败Security框架会抛出异常
    return new ResultVO(StatusCode.SUCCESS);

}

CSRF

  • CSRF指的是Cross-Site Request Forgery,中文翻译为“跨站请求伪造”,也称跨域攻击。它是一种网络攻击方式,攻击者利用用户在其他网站上登录过的身份信息,在用户不知情的情况下发送恶意请求来执行非法操作。

  • 攻击流程如下:

  • 为了防止CSRF攻击,通常需要采取一些措施,如增加验证码、使用CSRF Token、验证Referer等。

关闭跨域攻击防御机制

  • SpringSecurity框架默认开启了跨域攻击的防护,通过携带CSRF token对请求进行判断
  • 因为现在都是前后端分离架构,客户端发出的请求都是异步请求,不存在form表单,所以不存在跨域攻击的问题,通过以下代码关闭跨域攻击,否则请求受限
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //禁用“防止伪造的跨域攻击”的防御机制
        http.csrf().disable();
    }
}

通过真实数据进行登录

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired(required = false)
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserVO user = userMapper.selectByUsername(username);
        if (user == null) {
            return null;    //默认抛出 AuthenticationException 用户名找不到异常
        }

        UserDetails userDetail = User.builder()
            .username(username).password(user.getPassword())
            .disabled(false) //账号是否禁用
            .accountLocked(false)   //账号是否锁定
            .accountExpired(false)  //账号是否过期
            .credentialsExpired(false) //凭证是否过期
            .authorities("临时使用的权限") //权限
            .build();

        //如果用户输入的密码和数据库中查询到的密码不一致则会抛出异常
        return userDetail;
    }
}

统一异常处理

  • 对用户名、密码错误导致抛出的异常进行统一异常处理
package com.liner.exception;


import com.liner.response.ResultVO;
import com.liner.response.StatusCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@Slf4j //lombok提供的日志注解,在代码层面会为我们提供一个org.slf4j.Logger对象
@RestControllerAdvice //=@ControllerAdvice+@ResponseBody
public class GlobalExceptionHandler {
 
    @ExceptionHandler({InternalAuthenticationServiceException.class, BadCredentialsException.class})
    public ResultVO handleAuthenticationServiceException(AuthenticationException e){
        log.error("异常信息 is {}",e.getMessage());//日志级别trace<debug<info<warn<error
        if (e instanceof InternalAuthenticationServiceException){
            log.warn("用户名不存在!!!");
            return new ResultVO(StatusCode.USERNAME_ERROR,e.getMessage());
        }
        log.warn("密码错误!!!");
        return new ResultVO(StatusCode.PASSWORD_ERROR,e.getMessage());
    }
}

Security框架认证流程总结

  1. SecurityConfig配置类中配置好白名单,设置登录页面,关闭跨域攻击防御策略

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

  2. 当客户端请求路径不在白名单中,Security框架会自动将请求重定向到登录页面

  3. login.html登录页面中向/login地址发送登录请求时,服务器中UserController里面的login方法处理该请求

  4. login方法中通过认证管理器manager启动认证,将认证结果保存在Security上下文对象中

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

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

  5. manager启动认证流程后会自动调用UserDetailServiceImpl里面的loadUserByUsername方法,在方法内部,调用UserMapper里面的查询方法通过用户名查询到UserVO

  6. 如果查询不到return null,此时Security框架会抛出异常代表用户名不存在,需要全局异常处理进行处理,如果查询到的密码和用户输入的密码一致,则不抛出异常

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

  7. UserController中的login方法会执行完,给客户端响应登录成功的信息,如果登录的密码错误,Security框架会抛出代表密码错误的异常,此时也需要全局异常处理类进行处理

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

教师总结

同步请求和异步请求

  • 同步: 指单线程依次做几件事
  • 异步: 指多线程同时做几件事
  • 同步请求: 指客户端浏览器只有一个主线程,主线程既要负责页面渲染\监听用户的页面操作还需要负责发请求, 当主线程发出请求时,会将页面中的内容清空,知道服务器响应了数据之后再将响应的数据展示出来, 这个过程称为页面的整体刷新, 同步请求无法实现页面的局部刷新
  • 异步请求: 指客户端浏览器由主线程负责页面渲染和监听用户的页面的操作, 由子线程负责发出请求, 当子线程请求到数据之后,可以把得到的数据显示到原页面中, 这就是页面的局部刷新.

创建SpringBoot工程

  • 工程名boot01 打3个勾
  • 在pom.xml添加以下依赖 并刷新maven
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

<!--添加Knife4j依赖-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>

<!--Spring Validation依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  • 在application.properties里面添加以下配置信息
spring.datasource.url=jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
# 设置MyBatis框架的映射(Mapper)配置文件的位置
mybatis.mapper-locations=classpath:mappers/*.xml

logging.level.cn.tedu=debug

JSON

  • JSON 是一种轻量级的数据交换格式(也称为数据封装格式)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wnERl8QG-1685441210339)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530113359227.png)]

  • 当服务器给客户端响应复杂数据时需要将数据封装到Java对象里面, 但是Java对象不是跨平台的对象, 客户端是无法直接识别Java格式的对象的, 这时候就需要将Java的对象先转成JSON格式的字符串, 因为字符串是跨平台的类型, 客户端接收到JSON格式的字符串时,Axios框架会自动将其转成JS语言的对象 .

SpringSecurity框架流程

  1. 在工程中的pom.xml里面添加依赖
  2. 创建一个SecurityConfig.java配置类, 在里面重写configure方法
  3. 在方法中配置白名单

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7rLvOlq-1685441210340)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530160349272.png)]

  1. 创建UserDetailsServiceImpl实现类, 在里面配置正确的用户名和密码

​		[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hhq3m9CA-1685441210341)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530160314925.png)]

  1. 将默认的密码编码器设置为无加密

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qK6VZJUs-1685441210342)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530160234691.png)]

  1. 配置自己的登录页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pCHAe8HM-1685441210343)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530162523712.png)]

  2. 使用自己的登录页面时,Security框架的登录认证流程不会自动启动,需要我们在UserController处理login请求时手动开启,手动开启时需要用到认证管理器,在Security配置类中进行配置

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dpuPmdUx-1685441210344)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530162856261.png)]

​ 在UserController中自动装配认证管理器对象,并在login方法中启动认证流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-By2kXrql-1685441210346)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530165425169.png)]

  1. 关闭跨域攻击防御

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YQ4jnir-1685441210347)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530165542503.png)]

  1. UserDetailServiceImpl里面添加从数据库中查询数据的代码

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PvV1FkN-1685441210348)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530174734134.png)]

  2. 当登录失败时Security框架抛出了两种异常,需要在全局异常处理类中添加处理方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwkGXwFn-1685441210349)(C:\Users\TEACHER\Downloads\jsd-2303-04-teacher-master\文件\笔记\image-20230530174839215.png)]

Security框架认证流程:

  1. 在SecurityConfig配置类中配置好白名单, 设置登录页面, 关闭跨域攻击防御策略
  2. 当客户端请求的路径不在白名单里面, Security框架会自动将请求重定向到登录页面
  3. 在login.html登录页面中向/login地址发出登录请求时,服务器中UserController里面的login方法处理该请求
  4. 在login方法中通过认证管理器manager启动认证 将认证结果保存在Security上下文对象中
  5. 当manager启动认证流程后会自动调用UserDetailServiceImpl里面的loadUserByUsername方法, 在方法内部 调用UserMapper里面的查询方法通过用户名查询到UserVO, 如果查询不到return null, 此时Security框架会抛出异常代表用户名不存在,需要在全局异常处理中进行处理, 如果查询到的密码和用户输入的密码一致,则不会抛出任何异常 UserController中的login方法会执行完,给客户端响应登录成功的信息, 如果登录的密码错误Security框架会抛出代表密码错误的异常,此时也需要在全局异常处理类中进行处理.
举报

相关推荐

0 条评论