org.springframework.boot spring-boot-starter-security
在spring最新版中禁用了WebSecurityConfigurerAdapter类,官方推荐采用配置类的方法进行配置。
@EnableWebSecurity
在类中添加一个Bean,生成过滤方法
@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception{http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated()).httpBasic(withDefaults());return http.build();}

忽略某个路径下的请求
@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/level2", "/level3");}
效果就是当我请求http://localhost:8080/level2的时候,就会重复跳出登录页面


该类实现了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());}@Overridepublic Collection extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getUsername() {return super.getUsername();//把这里改成父类返回的username}@Overridepublic boolean isAccountNonExpired() {return true;//改成true}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
这个类是用来检索用户名和密码的,该类被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@Resourceprivate UserRepository userRepository;//根据username查询user@Overridepublic 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,而这行代码的功能正是如此:

在前面的代码中,loadUserByUsername 中的这行代码还没有实现其中的查询逻辑,接下来进行实现。

编写一个类实现findUserByUsername接口

在数据库进行User的查找就可以在这一步中实现了
package com.wjj.security.domain;import java.util.Map;public class MyUserRepository implements UserRepository{private Map usernameToUser;public MyUserRepository(Map usernameToUser){this.usernameToUser = usernameToUser;}@Overridepublic MyUser findUserByUsername(String username) {return usernameToUser.get(username);}
}
然后将这个MyUser变成Spring的一个Resource,因为我们前面在CustomUserDetailService中引用UserRepository时用到了@Resource注释:

在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);}@BeanMyUserRepository myUserRepository(){MyUser user = new MyUser(1L, "username", "{bcrypt}$2a$10$h/AJueu7Xt9yh3qYuAXtk.WZJ544Uc2kdOKlHu2qQzCh/A3rq46qm");Map myUserMap = new HashMap<>();myUserMap.put("username", user);return new MyUserRepository(myUserMap);}}
代码中的那堆字母时经过加密的密码,在前端用户输入密码之后传到后端时进行了加密。

登陆成功:
