入门案例
maven开发时spring boot框架时,引入如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
或者在建立模块时直接选择如下选项导入.
应用建立后,写一个简单的controller,访问后会跳到如下的登录页面:
登录账号是:user
密码见日志:
输入账号和密码后,正确跳转:
此时,所有的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用户存储。
见依赖:
使用基于内存用户存储做一个案例,结合上面的密码转换器完整配置代码如下:
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);
}
}
运行程序,如下:
同时,密码转换器和用户存储方式可以开发自行定义.定义一个简单的基于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);
}
}
运行应用,看日志如下:
自定义用户存储
为了简单,决定采用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>
库表和建表语句及默认数据
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加密密文.检验效果成功.