Spring Security学习01

130 阅读3分钟

入门案例

maven开发时spring boot框架时,引入如下依赖:

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

或者在建立模块时直接选择如下选项导入.

jym01.png

应用建立后,写一个简单的controller,访问后会跳到如下的登录页面:

jym02.png

jym03.png 登录账号是:user

密码见日志:

jym04.png

输入账号和密码后,正确跳转:

jym05.png

此时,所有的HTTP请求路径都需要认证;没有特定的角色和权限;认证过程是通过弹出一个简单的登录页实现的;系统只有一个登录用户,用户名为user。

为保证应用的安全性,至少要实现如下的功能: 提供应用自己的登录页面,提供用户注册功能,不同的请求路径执行不同的安全规则. 为此我们需要做一些配置.

相关配置

为了代码简单点决定采用基于java的配置

密码转换器

为避免我们的被泄露,spring security支持对密码进行加密.

@Configuration
@Slf4j
public class SecurityConfig {
    /**
     * 密码转换器
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        log.info("passwordEncoder start");
        return new BCryptPasswordEncoder();
    }
}

以上配置采用了BCryptPasswordEncoder使用bcrypt强哈希加密.spring security提供了以下多个密码转换器:

●BCryptPasswordEncoder:使用bcrypt强哈希加密。

●NoOpPasswordEncoder:不使用任何转码。

●Pbkdf2PasswordEncoder:使用PBKDF2加密。

●SCryptPasswordEncoder:使用Scrypt哈希加密。

●StandardPasswordEncoder:使用SHA-256哈希加密。

设置用户

为认证功能匹配用户时我们需要声明一个UserDetailsService的bean,实际上spring security已经内置了多种实现:

●内存用户存储;

●JDBC用户存储;

●LDAP用户存储。

见依赖:

jym06.png

使用基于内存用户存储做一个案例,结合上面的密码转换器完整配置代码如下:

package com.by.springsecurityaction.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

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

/**
 * @author: 代码民工
 * @date: 2023/12/3 15:39
 * @description:
 */
@Configuration
@Slf4j
public class SecurityConfig {
    /**
     * 密码转换器
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        log.info("passwordEncoder start");
        return new BCryptPasswordEncoder();
    }
    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder){
        List<UserDetails> userDetails = new ArrayList<>();
        userDetails.add(new User("lisi",passwordEncoder.encode("123456"),
                Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))));
        log.info("userDetailsService start");
        return new InMemoryUserDetailsManager(userDetails);
    }
}

运行程序,如下:

jym07.png

jym08.png

jym09.png

同时,密码转换器和用户存储方式可以开发自行定义.定义一个简单的基于MD5工具的密码转换器(需要实现PasswordEncoder接口).

package com.by.springsecurityaction.config;

import com.by.springsecurityaction.util.MD5Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author: 代码民工
 * @date: 2023/12/3 15:55
 * @description:
 */
@Slf4j
public class OwnPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {
        log.info("OwnPasswordEncoder old rawPassword : {}", rawPassword.toString());
        String md5 = MD5Utils.MD5(rawPassword.toString());
        log.info("OwnPasswordEncoder encode md5 : {}", md5);
        return md5;
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        log.info("OwnPasswordEncoder mds encodedPassword : {} ; input str : {}", encodedPassword, rawPassword.toString());
        String inputMd5 = MD5Utils.MD5(rawPassword.toString());
        log.info("OwnPasswordEncoder matches inputMd5 : {}", inputMd5);
        return encodedPassword.equals(inputMd5);
    }

}

运行应用,看日志如下:

jym10.png

自定义用户存储

为了简单,决定采用H2作为数据库,创建用户表.引入依赖和创建库表如下:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

库表和建表语句及默认数据

jym11.png

create table if not exists MY_USER (
  id identity,
  username varchar(25) not null,
  password varchar(25) not null
);
delete from MY_USER ;
insert into MY_USER (username, password)
                values ('lisi','e10adc3949ba59abbe56e057f20f883e');

最终代码,使用了自己定义的加密器和用户信息存储.

    /**密码转换器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        log.info("passwordEncoder start");
//        return new BCryptPasswordEncoder();
        //自定义的加密器
        return new OwnPasswordEncoder();
    }
    
    @Bean
    public UserDetailsService userDetailsService(UserRepository userRepo) {
        return username -> {
            log.info("userDetailsService  start username : {}",username);
            MyUser user = userRepo.findByUsername(username);
            if (user != null) {
                Account account = new Account();
                account.setUser(user);
                return account;
            };

            throw new UsernameNotFoundException("User '" + username + "' not found");
        };
    }

这一串e10adc3949ba59abbe56e057f20f883e密文是123456的md5加密密文.检验效果成功.