您好,欢迎来到叨叨游戏网。
搜索
您的当前位置:首页SpringSecurity简单自定义配置

SpringSecurity简单自定义配置

来源:叨叨游戏网


一、环境搭建&相关依赖(无指定版本对应SpringBootVersion)

1.SpringBoot 3.3.1

2.spring-boot-starter-security

3.spring-boot-starter-thymeleaf

4.thymeleaf-extras-springsecurity6

5.spring-boot-starter-web

6.lombok

7.spring-boot-starter-test

8.spring-security-test

9.mysql-connector-java(8.0.30 根据自己)

10.mybatis-plus-boot-starter 3.5.5

11.mybatis-spring 3.0.3

12.maven -3.9.8

二、基于内存的用户认证

基于内存的用户认证执行流程

程序启动时:

校验用户时:

  1. SpringSecurity自动使用InMemoryUserDetailsManager的loadUserByUsername 方法从内存中 获取

User对象

  1. 在UsernamePasswordAuthenticationFilter 过滤器中的atemptAuthentication方法中将用户输入的用

户名密码和从内存中获取到的用户信息进行比较,进行用户认证

创建基础的Controller

@Controller
public class    IndexController {
    @GetMapping("/")
    public String index(){
        return "index";
    }
}

静态资源index.html(基于thymeleaf编写)

<html xml:th="https://www.thymeleaf.org">
<head>
    <title>
        Hello Spring Security
    </title>
</head>
<body>
 <h1>Hello Security</h1>
    <a th:href="@{/logout}">Log Out 1 </a>
    <a href="/logout">Log Out 2</a>
</body>
</html>

SecurityConfiguration配置类 指定username和password

@Configuration //配置类
@EnableWebSecurity  //开启springSecurity的自定义配置
public class WebSecurityConfig {
    @Bean
    public UserDetailsService userDetailsService() {
        //创建一个基于内存的用户管理器
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //创建UserDetails对象,用于管理用户名,用户密码,用户角色,用户权限等内容
        inMemoryUserDetailsManager.createUser(User.withDefaultPasswordEncoder().username("user").password("123").roles("User").build());
        return inMemoryUserDetailsManager;
    }
}

测试 浏览器输入http://localhost:8080跳转到login界面

三、基于数据库的用户认证

环境搭建比较麻烦,自己在数据库中创建一个user表都可以实现案例。建议使用mybatis-x插件快速生成案例环境。

基于数据库的用户认证流程

程序启动时:

  1. 创建DBUserDetailsManager对象,实现接囗UserDetailsManager, UserDetailsPasswordService

校验用户时:

  1. SpringSecurity自动使用DBUserDetailsManager的loaduserByUsername 方法从数据库中 获取User对象
  2. 在UsernamePasswordAuthenricationFilter 过滤器中的attemptAuthentication方法中将用户输入的用户名密码和从内存中获取到的用户信息进行比较,进行用户认证

如下图项目结构

创建DBUserDetailsManager 模仿InMemoryUserDetailsManager 进行编写

@Component
@SuppressWarnings({"all"})
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {
    @Resource
    private userMapper userMapper;
    
    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        return null;
    }

    @Override
    public void createUser(UserDetails user) {

    }

    @Override
    public void updateUser(UserDetails user) {

    }

    @Override
    public void deleteUser(String username) {

    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {

    }

    @Override
    public boolean userExists(String username) {
        return false;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<? extends GrantedAuthority> collection=new ArrayList<>();
        //根据用户输入在数据库中查询对应的用户
        QueryWrapper<user> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.ge("username", username);
        user user = userMapper.selectOne(userQueryWrapper);
        if(user==null){
            throw new UsernameNotFoundException(username);
        }else return new User(
                user.getUsername(),
                user.getPassword(), 
                user.getEnabled(), 
                true, //用户账户是否过期
                true,//用户凭证是否过期
                true,//用户是否未被锁定
                collection); //权限列表
    }
}

测试

四、添加用户(数据库)

添加userController

@RestController
@RequestMapping("/user")
@Slf4j
public class userController {

    @Autowired
    private userService userService;

    @PostMapping("add")
    public String saveUser(@RequestBody user user){
        log.info(user.toString());//接受到的user对象实例
        userService.saveUserDetails(user);
        return "添加成功";
    }
}

实现saveUserDetails方法 不能单纯的使用mybatis-plus的save方法

@Service
public class userServiceImpl extends ServiceImpl<userMapper, user>
    implements userService{

    @Autowired
    private DBUserDetailsManager dbUserDetailsManager;

    @Override
    public void saveUserDetails(user user) {
        UserDetails userDetails = User.withDefaultPasswordEncoder().username(user.getUsername()).password(user.getPassword()).build();
        dbUserDetailsManager.createUser(userDetails);
    }
}

重写DbuserDetailsManager中的createUser方法

    @Override
    public void createUser(UserDetails user) {
        user user1 = new user();
        user1.setUsername(user.getUsername());
        user1.setPassword(user.getPassword());
        user1.setEnabled(true);
        userMapper.insert(user1);
        log.info("添加成功");
    }

测试 由于security默认防止csrf攻击,所以需要关闭csrf攻击进行测试

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //用于开启授权保护
        http.authorizeRequests(
                        authorize->
                                authorize.anyRequest().//开启所有用户的授权保护
                                        authenticated()) //已认证的请求会被自动授权
                .formLogin(Customizer.withDefaults())//自动生成默认的登录页和登出页 使用表单授权方式
                .httpBasic(Customizer.withDefaults());//基本授权方式
        http.csrf(csrf-> csrf.disable()); //关闭csrf保护策略
        return http.build();
    }

补充:使用apifox进行端口测试 由于用户认证问题会造成401状态码,可以先浏览器登录用户认证 然后F12复制登录信息中的cookie值 放入到测试用例中即可完成测试

五、自定义密码加密

如果没有指定密码加密算法 默认执行的是BCrypt算法 下图指定使用SHA-1加密。

工厂的静态构造方法把常用的几种密码方案都注入到了缓存Map中,默认注入的 encodingId 对应的是 BCryptPasswordEncoder加密方案,这样系统就可以达到在新存储密码可以使用 BCryptPasswordEncoder 加密方案进行加密,但是对于数据库里面以前用其他方式加密的密码也支持比对。我们可以复写该方法,然后修改这个“encodingId”的值,就可以在几种加密算法中进行切换了。

@SpringBootTest
class SecurityDemoApplicationTests {

    public static PasswordEncoder createDelegatingPasswordEncoder() {
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put(encodingId, new BCryptPasswordEncoder());
        encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
        encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
        encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
        encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
        return new DelegatingPasswordEncoder("SHA-1", encoders);
    }

    @Test
    public void test1(){
        PasswordEncoder encoder = SecurityDemoApplicationTests.createDelegatingPasswordEncoder();
        String result = encoder.encode("zhanghaijie");//即使明文密码相同但是生成的密文也会不同
        System.out.println("result = " + result);
    }

}

六、自定义登录界面

指定Controller

@Controller
public class loginController {

    @GetMapping("/login")
    public String login() {
        return "login"; //跳转访问下方的静态资源
    }
}

Controller指定跳转到此自定义登录界面

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <title>登录</title>
</head>
<body>
<h1>登录</h1>
<div th:if="${param.error}">
    错误的用户名和密码.</div>

<!--method必须为"post"-->
<!--th:action="@{/login}" ,
使用动态参数,表单中会自动生成_csrf隐藏字段,用于防止csrf攻击
login: 和登录页面保持一致即可,SpringSecurity自动进行登录认证-->
<form th:action="@{/login}" method="post">
    <div>
        <!--name必须为"username"-->
        <input type="text" name="username" placeholder="用户名"/>
    </div>
    <div>
        <!--name必须为"password"-->
        <input type="password" name="password" placeholder="密码"/>
    </div>
    <input type="submit" value="登录" />
</form>
</body>
</html>

SecurityFilterChain中指定跳转的页面 注意login对应的是html文件名

/**
 * 此类是Security默认存在的Bean类
 */
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    //用于开启授权保护
    http.authorizeRequests(
        authorize->
        authorize.anyRequest().//开启所有用户的授权保护
        authenticated()) //已认证的请求会被自动授权
    .formLogin(form->{
        form.loginPage("/login").permitAll(); //无需授权即可访问当前页面
    }) //自定义登录界面
    .httpBasic(Customizer.withDefaults());//基本授权方式

    http.csrf(csrf-> csrf.disable()); //关闭csrf保护策略
    return http.build();
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务