Spring Security——03,认证_登录

一、思路分析

登录

​		1、自定义登录接口

​				调用ProviderManager的方法进行认证 如果认证通过生成jwt

​				把用户信息存入redis中

​	    2、自定义UserDetailsService

​				在这个实现类中去查询数据库

校验:

​		1、定义Jwt认证过滤器

​				获取token

​				解析token获取其中的userid

​				从redis中获取用户信息

​				存入SecurityContextHolder

疑问:最后为什么要存入SecurityContextHolder呢?

答:因为如果我们其他的过滤器、包括一些资源可能需要去获取当前登录用户信息的,所以我们要把它存到

SecurityContextHolder当前,让其他地方要用也可以用到。

FilterSecuritylnterceptor过滤器就会用到:请求经过这里的时候,如果还是一个未认证的状态,它就拦截下来了,它是怎么判断的呢?它会获取SecurityContextHolder中用户的一个认证信息,如果能获取到,并且是认证过的状态,它就会放行。

二、准备工作

2.1、重新创建一个项目

省略,完成后如图:

在这里插入图片描述
添加依赖:

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.15</version>
    </parent>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--security依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

    </dependencies>

创建启动类跟controller接口

在这里插入图片描述
然后启动测试一下,输入我们的hello接口,一样回跳到login页面

在这里插入图片描述

2.2、准备项目环境

直接下载

链接:https://pan.baidu.com/s/1FVSI0v3AOZAi8p_xNrvkHg
提取码:i81o

完成之后,总的目录是这样的

在这里插入图片描述

2.3、准备数据库

CREATE TABLE `sys_user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',
`nick_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',
`password` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',
`status` CHAR(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',
`email` VARCHAR(64) DEFAULT NULL COMMENT '邮箱',
`phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手机号',
`sex` CHAR(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',
`avatar` VARCHAR(128) DEFAULT NULL COMMENT '头像',
`user_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',
`create_by` BIGINT(20) DEFAULT NULL COMMENT '创建人的用户id',
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
`update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人',
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
`del_flag` INT(11) DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

然后创建一个测试类,测试一下,ok,没有问题

在这里插入图片描述

三、实现

3.1 数据库校验用户

创建一个类实现UserDetailsService接口,重写其中的方法。更加用户名从数据库中查询用户信息

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws
            UsernameNotFoundException {
        //根据用户名查询用户信息
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserName,username);
        User user = userMapper.selectOne(wrapper);
        //如果查询不到数据就通过抛出异常来给出提示
        if(Objects.isNull(user)){
            throw new RuntimeException("用户名或密码错误");
        }
        //TODO 根据用户查询权限信息 添加到LoginUser中

        //封装成UserDetails对象返回
        return new LoginUser(user);
    }
}

因为UserDetailsService方法的返回值是UserDetails类型,所以需要定义一个类,实现该接口,把用户

信息封装在其中。

把下面的返回false全部都改成true,然后name跟password分别返回对应的name,password

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
    private User user;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public String getPassword() {
        return user.getPassword();
    }
    @Override
    public String getUsername() {
        return user.getUserName();
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

页面登录一下,没反应,后台报错了。

在这里插入图片描述因为数据库是明文存储的,所以报错了。

在这里插入图片描述如果要测试,需要往用户表中写入用户数据,并且如果你想让用户的密码是明文存储,需要在密

码前加{noop}。如下:这样,再登录就不会有问题了。

在这里插入图片描述
如果填写错误的账号密码呢?就会返回用户名或者密码错误

在这里插入图片描述
这里有一个问题,我们没有校验密码,这是为什么呢?

答:其实我们也没有校验用户名,只是给了一个必要的校验信息而已。用了安全框架 是不需要你自己去校验的 你只需要将认证需要的必要信息 也就是用户信息 权限信息 封装到UserDetails里面就可以 校验是框架自己在干活。

3.2 数据库密码加密存储

实际项目中我们不会把密码明文存储在数据库中。

默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password 。它会根据id去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder。

我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。

我们只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该
PasswordEncoder来进行密码校验。

我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承
WebSecurityConfigurerAdapter。

在这里插入图片描述但我们发现这是一个过时的类,在springboot 2.7 以上就过时了

改用EnableWebSecurity注解

在这里插入图片描述然后,测试一下,有两个方法

encode();
	传入一个明文,返回一个密文
matches();
	第一个参数是明文,第二个参数是密码,如果匹配上就返回true,否则就是false

测试一下:

在这里插入图片描述
一切都没有问题,之后,重启服务,登录测试一下

数据库,存储的已经是密文了,但同样是可以登录成功了

在这里插入图片描述关于加密可以去搜索一下对应的加密算法,这里简单的叙述一下:

当我们输入密码1234后,Spring Security会根据存储的密码密文解析出加密算法和盐值,然后它会根据这个加密算法和盐值将你输入的密码加密,最后才会将生成的密码密文与存储的密码密文进行比较。

每次加密的随机盐会被保存在一个hash中的,每次加密时候,密文中都包含了随机盐的基本信息,所以他能根据这个基本信息找到保存在hash上的那个随机盐。所以是一一对应的关系。

3.3 登录接口

1、 修改一下SecurityConfig配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig{

    //配置密码加密器
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 安全配置
     */
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过session获取securitycontext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //对于登录接口,允许匿名访问
                .antMatchers("/user/login").anonymous()
                //除上面外的所有请求全部需要坚定认证
                .anyRequest().authenticated();
        return http.build();
    }

    /**
      * 认证管理器,登录的时候参数会传给 authenticationManager
      */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}

2、创建LoginController

@RestController
public class LoginController {

    @Autowired
    private LoginServcie loginServcie;

    @PostMapping("/user/login")
    public ResponseResult login(@RequestBody User user){
        System.out.println("11111");
        return loginServcie.login(user);
    }
}

3、LoginServcie接口

public interface LoginServcie {
    ResponseResult login(User user);
}

4、LoginServiceImpl实现类

登录实现类的三步走
//AuthenticationManager authenticate 进行用户认证
//如果认证通过了,使用userid生成一个jwt,jwt存入ResponseRsult
//把完整的用户信息存入redis,userid作为keys
@Service
public class LoginServiceImpl implements LoginServcie {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Override
    public ResponseResult login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken = new
              UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
        Authentication authenticate =
                authenticationManager.authenticate(authenticationToken);
        //使用userid生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        //把token响应给前端
        HashMap<String,String> map = new HashMap<>();
        map.put("token",jwt);
        return new ResponseResult(200,"登陆成功",map);
    }
}

对第二句代码的一个小解析:

它会调用我们之前自定义的UserDetailsServiceimpl里面的方法进行一个校验,会根据我们传过去的用户名去查询数据库,然后再返回

做完了之后,重启一下项目,使用poetman测试一下效果

OK,没有 任何问题。

在这里插入图片描述测试ok了,还有一步没有做,用户信息没有存入redis

3.4 用户信息存入redis

因为我已经搭建、整合好redis哨兵集群了,这里就省略了redis过程了

 	@Autowired
    private RedisTemplate redisTemplate;

在这里插入图片描述

四、完结

至此,认证—登录,部分完成

在这里插入图片描述

一键三连有没有捏~~

  • 48
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security基于数据库认证是通过将用户信息存储在数据库中进行认证的一种方式。首先,我们需要根据Spring Security框架的定义要求,创建相关的数据表来存储用户信息[2]。接下来,我们可以使用org.springframework.security.core.userdetails.UserDetailsService包路径下的接口进行用户信息的获取认证。通过实现这个接口,并在其中编写逻辑代码,我们可以从数据库中获取用户信息,并进行相应的认证操作。在数据库中存储用户信息的方式可以是明文存储、加密存储或者使用其他安全方式进行存储。通过这种基于数据库的认证方式,Spring Security可以实现灵活可靠的用户认证功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Spring Security——基于数据库的用户信息认证](https://blog.csdn.net/qq_40151840/article/details/104620157)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [SpringSecurity 基于数据库的验证](https://blog.csdn.net/qq_41723615/article/details/89512333)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值