springSecurity自定义用户认证逻辑
1. 创建项目
使用idea
中的spring
初始化工具引入springboot
和springsecruity
初始化项目
最终pom.xml
如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-spring-secruity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-spring-secruity</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建一个测试控制器
package com.example.demospringsecruity.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author john
* @date 2020/1/6 - 9:47
*/
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello spring secruity";
}
}
运行项目
访问<http://localhost:8081/hello>
,会被跳转到<http://localhost:8081/login>
输入用户名user
和刚才运行项目时输出的密码,登录成功,可以获取到刚才预期的响应
2. 自定义登录方式
package com.example.demospringsecruity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author john
* @date 2020/1/6 - 10:07
*/
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表单登录
// http.formLogin()
// 基本登录
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
当浏览器发送一个请求后,首先会经过第一个过滤器,UsernamePassowordAuthenticationFilter
,这个过滤器中,它会判断你的请求是否带username
和password
这两个参数,如果带了就进行拦截验证,如果没有就进入到第二个过滤器BasicAuthenticationFilter
,这个过滤器会判断请求头中是否含有需要验证的信息,如果没有进入下一个拦截器,最后到ExceptionTranslationFilter
拦截器,它会捕获FilterSecurityInterceptor
抛出的异常,而FilterSecurityInterceptor
会判断这个请求是否校验通过,权限是否通过,以上就是Spring Security
框架的一个基本认证流程,FilterSecurityInterceptor
是这个框架的最后一个拦截器,所有的请求都必须通过该拦截器的校验。
3. 自定义用户认证逻辑
1. 处理用户信息获取逻辑 UserDetailsService
2. 处理用户校验逻辑 UserDetails
3. 处理密码加密解密 PasswordEncoder
UserDetailsService
用户信息获取逻辑在springsecruity
中是被封装在UserDetailService
接口中
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
UserDetails
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();//权限的集合
String getPassword();//密码
String getUsername();//用户名
boolean isAccountNonExpired();//账户是否没过期 true-->没过期
boolean isAccountNonLocked();//账户是否没锁定 true-->没被锁定
boolean isCredentialsNonExpired();//凭证是否没过期 true-->没过期
boolean isEnabled();//账户是否可用
}
下面开始编写具体代码
MyUserDetailsService处理用户信息获取逻辑
package com.example.demospringsecruity.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* @author john
* @date 2020/1/6 - 10:32
*/
@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {
//这里可以注入mapper或者repository的dao对象来实现数据校验逻辑操作
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("用户名:" + username);
//这里密码应该从数据库中取出,暂时先使用加密生成
String password = passwordEncoder.encode("123456");
//User类是Spring内置的一个类,实现了UserDetails接口,而这个接口是UserDetailSerice的子接口
return new User(username,
password,
true, // 账户是否可用
true, // 账户是否过期
true, // 密码是否过期
true, //账户是否被锁定
AuthorityUtils.commaSeparatedStringToAuthorityList("admin") //授权集合
);
}
}
PasswordConfig处理密码加密解密
注入PasswordEncoder
package com.example.demospringsecruity.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author john
* @date 2020/1/6 - 12:51
*/
//说明这个一个配置类,类似spring中的xml文件
@Configuration
public class PasswordConfig {
//手动将PasswordEncoder注入到ioc容器中
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
测试在输入用户user
密码123456
后成功获取数据
上面的演示我使用了spring-security
自己的UserDetails
实现类User
(org.springframework.security.core.userdetails.User
),但实际开发中我们不可能这么用,因为根据实际的业务,往往会希望用户信息表里会包含更多的数据,比如说电话,邮箱,公司职位等信息,那我们该如何去处理呢?
其实非常简单,我们只要将我们自己的User
表继承spring-security
的User
类(org.springframework.security.core.userdetails.User
),再在MyUserDetailsService
里只要返回我们自己的user
即可.