携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
为什么需要这个框架
- 后端的接口不方便对外开放,需要后端做权限校验。例如后台管理系统接口访问需要 token,或不同角色需要访问不同的内容等。
- 拥抱 Spring 开源,学习 Spring Security 为后续微服务中的 Oauth2 打下基础。
创建项目
打开 IDEA 工具,操作菜单 文件 -> 新建 -> 项目
点击
下一步, 点击 创建
最终项目目录
引入 Maven 依赖
<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>
<optional>true</optional>
</dependency>
tips: Maven 下载与加载依赖很慢,建议喝口茶等等,不推荐阿里源,之前踩过坑,静心慢慢等一会。
安全配置
目前很多文章,是老版本,新版本的 Spring Security 弃用了继承 WebSecurityConfigurerAdapter.
更推荐使用 lamda 风格编写的代码风格,与组件化配置。
相关文献见链接:spring.io/blog/2022/0…
根据 Spring Security 5.7.0-M2 后版本建议,新建 SecurityConfiguration.java 配置类,如下:
粘贴文件后,你会发现存在错误,不要慌,后面有此代码引用的类放上
package com.example.auth.config;
import com.example.auth.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* desc: 保护接口组件
* date 2022/7/25
*
* @author 程序员鱼丸
**/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private static final String[] IGNORE_API = new String[]{"/user", "/login"};
// 引入用户服务业务层, 使用 setter 注入
private AdminService userDetailsService;
@Autowired
public void setUserDetailsService(AdminService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authz ->
// 放行一些接口,在 IGNORE_API 中
authz.antMatchers(IGNORE_API).permitAll()
// 除了放行的接口其他全校验
.anyRequest().authenticated()
)
// 使用 Spring Security 提供的默认值启用安全功能
.httpBasic(withDefaults());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
// 密码的加密方式
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
// 指定密码加密方式
authProvider.setPasswordEncoder(passwordEncoder());
// 指定用户业务层,此业务层需要实现 Spring Security 官方的 UserDetailsService 接口
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
}
实体类与业务实现类
实体类: AdminUser.java
package com.example.auth.domain;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
/**
* desc: 需要实现官方接口 UserDetails
* date 2022/7/25 15:57
*
* @author 程序员鱼丸
**/
@Data
public class AdminUser implements UserDetails {
private String username;
private String password;
/**
* 账户的权限,例如 ["ROLE_ADMIN","ROLE_USER"]
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
/**
* 帐户未过期
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 帐户未锁定
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 帐户未过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 帐户是否启用
*/
@Override
public boolean isEnabled() {
return true;
}
}
业务实现类:AdminService.java
package com.example.auth.service;
import com.example.auth.domain.AdminUser;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AdminService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
// 这里伪代码查询数据库
if ("admin".equals(username)) {
AdminUser adminUser = new AdminUser();
adminUser.setUsername("admin");
adminUser.setPassword(new BCryptPasswordEncoder().encode("123456"));
return adminUser;
} else {
throw new UsernameNotFoundException("用户名密码错误");
}
}
}
提供 API 用来测试权限
控制层访问类:ApiController.java
package com.example.auth.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ApiController {
@GetMapping("admin")
public String getAdmin() {
return "需要登录验证权限";
}
@GetMapping("user")
public String getUser() {
return "不需要登录验证权限";
}
}
启动 Spring Boot 项目并访问接口测试
tips: 不会启动的去网页搜一下,idea 配置 springboot 启动.
首先,我们先访问: http://127.0.0.1:13921/user, 很显然直接能访问到
为啥呢?就知道你复制的时候没看代码,带你复习下
我们看到上面,user 接口被我们放行了,下面我们来请求 admin 接口
提示需要登陆了,我们输入在业务实现类中配置的用户名密码,不记得的回去看看
可以看到能访问到了,此时用的是 session 鉴权,这节课就先到这里
总结
代码 Git 仓库:github.com/cuifuan/aut…
-
- 编写 Security 安全配置类
-
- 实现 UserDetails 接口自定义 loadUserByUsername 接口
-
- 自定义修改安全配置类
展望未来
现如今,前后端分离是主要趋势,我们的登录接口后面需要定义为 Restful 风格登录,并对一些错误进行自定义。
- 登录接口 Restful 化
- 错误自定义
- 集成 JWT