1 SpringSecurity
1.1 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2 编写配置类
在spring最新版中禁用了WebSecurityConfigurerAdapter类,官方推荐采用配置类的方法进行配置。
- 新建一个配置类,注意添加注释
@EnableWebSecurity
1.3 示例
-
在类中添加一个Bean,生成过滤方法
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ http .authorizeHttpRequests((authz) -> authz .anyRequest().authenticated() ) .httpBasic(withDefaults()); return http.build(); }
-
忽略某个路径下的请求
@Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers("/level2", "/level3"); }
效果就是当我请求
http://localhost:8080/level2
的时候,就会重复跳出登录页面
1.4 自定义User实现登录
1.4.1 编写User类
1.4.2 定义UserRepository接口,用来实现从数据库或其它地方查询User
1.4.3 自定义一个类实现UserDetails接口
该类实现了UserDetails
接口,这个UserDetails
接口就是来查找用户名什么的。下面创建一个类,它继承了自定义的MyUser
类且实现了UserDetails
接口。(为什么要继承MyUser
类?因为官方示例这么做的。。。)
package com.wjj.security.domain;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class MyUserDetails extends MyUser implements UserDetails {
//定义构造函数
public MyUserDetails(MyUser myUser){
super(myUser.getId(), myUser.getUsername(), myUser.getPassword());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getUsername() {
return super.getUsername();//把这里改成父类返回的username
}
@Override
public boolean isAccountNonExpired() {
return true;//改成true
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
1.4.4 定义一个类实现UserDetailsService接口
这个类是用来检索用户名和密码的,该类被DaoAuthenticationProvider
调用,至于DaoAuthenticationProvider
是什么可以查看官方文档。
package com.wjj.security.service;
import com.wjj.security.domain.MyUser;
import com.wjj.security.domain.UserRepository;
import jakarta.annotation.Resource;
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 CustomUserDetailService implements UserDetailsService {
//注入编写的UserRepository
@Resource
private UserRepository userRepository;
//根据username查询user
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = userRepository.findUserByUsername(username);
if(user == null){
throw new UsernameNotFoundException(username + " Not found");
}
//要返回一个UserDetails
//这个东西就是前面实现的那个类
//用来验证该user是否有效
return new MyUserDetails(user);
}
}
可以看到本节的功能就是根据username寻找User,然后返回一个UserDetails
。我甚至可以在上面的函数中直接从数据库或内存中查询User,而这行代码的功能正是如此:
1.4.5 实现UserRepository接口进行查找用户
在前面的代码中,loadUserByUsername
中的这行代码还没有实现其中的查询逻辑,接下来进行实现。
编写一个类实现findUserByUsername
接口
在数据库进行User的查找就可以在这一步中实现了
package com.wjj.security.domain;
import java.util.Map;
public class MyUserRepository implements UserRepository{
private Map<String, MyUser> usernameToUser;
public MyUserRepository(Map<String, MyUser> usernameToUser){
this.usernameToUser = usernameToUser;
}
@Override
public MyUser findUserByUsername(String username) {
return usernameToUser.get(username);
}
}
然后将这个MyUser变成Spring的一个Resource,因为我们前面在CustomUserDetailService
中引用UserRepository
时用到了@Resource注释:
1.4.6 创建内存用户
在Application中new一个MyUserRepository
实例,作为一个内存中存在的用户,并把它交给Spring托管:
package com.wjj.security;
import com.wjj.security.domain.MyUser;
import com.wjj.security.domain.MyUserRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
@Bean
MyUserRepository myUserRepository(){
MyUser user = new MyUser(1L, "username", "{bcrypt}$2a$10$h/AJueu7Xt9yh3qYuAXtk.WZJ544Uc2kdOKlHu2qQzCh/A3rq46qm");
Map<String, MyUser> myUserMap = new HashMap<>();
myUserMap.put("username", user);
return new MyUserRepository(myUserMap);
}
}
代码中的那堆字母时经过加密的密码,在前端用户输入密码之后传到后端时进行了加密。
1.4.7 启动项目进行登录
登陆成功: