SpringSecurity表单认证(二)

用户名+密码

系统默认登录用户名:user

密码每次服务启动后随机生成密码

用户信息获取原理(数据库获取)

实现该接口,security默认自动生成密码关闭。框架源码:

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

获取用户信息逻辑,封装在security的UserDetailsService接口,实现该接口只有一个方法UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

该方法接收用户登陆时的username,根据username可以从数据库,redis,LDAP等读取用户信息,用户信息封装到UserDetails接口的实现类中,实现类User;

框架源码

复制代码
public class User implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = 530L;
    private static final Log logger = LogFactory.getLog(User.class);
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
复制代码

User.UserBuilder设置用户名,密码,权限信息返回UserDetails; loadUserByUsername返回UserDetails,security接收并以此来验证访问是否有效.

密码加密解密

框架源码

自定义密码验证(密码加密验证器)

复制代码
public interface PasswordEncoder {
    String encode(CharSequence rawPassword);//用来加密;原始密码加密后的值,前端传过来的明文密码

    boolean matches(CharSequence rawPassword, String encodedPassword);//加密后的密码与用户上传的密码(明文)是否匹配

    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}
复制代码

org.springframework.security.crypto.bcrypt;包实现

可自定义实现该接口

实际场景中,用户密码是加密后传输,保存的密码都是加密的

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http.formLogin()  //表单登陆认证方法,浏览器跳转到specurity默认登陆页面
        http.httpBasic()//浏览器弹出登陆对话框登陆认证方式
        .and()
        .authorizeRequests() ////设置请求符合访问资源的权限
        .anyRequest().authenticated(); //对任何请求都要登陆认证后才能访问
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance(); //已弃用
        return new BCryptPasswordEncoder();
    }
}
复制代码

处理用户校验

用户密码校验
复制代码
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder pw;

    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //TODO 查询用户信息(mysql,redis,ldap)
        User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getName, s));
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        String pwd = pw.encode(user.getPasswd());
    //TODO 从数据库获取用户相关权限
        String[] authorities = {"admin", "test"};//在configure方法中设置验证权限
        UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(pwd).authorities(authorities).build();
        return userDetails;
    }
}
复制代码

设置用户帐户过期校验

实现UserDetailsService接口的User类的boolean isAccountNonExpired()方法。

根据数据库表中设计来设定用户过期时间。

如果当前时间大于过期时间,说明用户已过期,设置User.UserBuilder.accountExpired(true),默认为false

复制代码
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder pw;

    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //TODO 查询用户信息(mysql,redis,ldap)
        User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getName, s));
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        String pwd = pw.encode(user.getPasswd());
        //TODO 从数据库获取用户相关权限
        String[] authorities = {"admin", "test"}; //在configure方法中设置验证权限
        org.springframework.security.core.userdetails.User.UserBuilder builder = org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(pwd).authorities(authorities);
        //查询用户的信息中created表示为过期时间,判断created是否小于当前时间
        Long currentTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        Long expireTime = user.getCreated().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        if (currentTime>expireTime){
            builder.accountExpired(true);
        }
        //UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(pwd).authorities(authorities).build();
        UserDetails userDetails = builder.build();
        return userDetails;
    }
复制代码
设置用户密码过期校验

与设置用户帐户过期校验逻辑一样,默认为false

User.UserBuilder.credentialsExpired(true);

设置用户锁定校验

与设置用户帐户过期校验逻辑一样,默认为false

User.UserBuilder accountLocked(true)

设置用户是否可用校验

User.UserBuilder disabled(true)

校验总结

boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();

User类对这四个方法的实现只是返回false或true,判断逻辑是自己判断出结果,通过调置User的属性值是false或true,在由这几个方法返回;

自己可实现UserDetails接口,将逻辑判断直接写入这几个方法中。

登陆成功,客户端将JSESSIONID保存在Cookie里,每次请求携带

"Cookie": "JSESSIONID=1C6C3BD094405290E5802CB98F627E19"

posted @   wangzhilei  阅读(13)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示