stater
//依赖导入
org.springframework.boot
spring-boot-starter-security
写一个controller类测试
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("hello")
public String hello(){
return "hello";
}
}
运行成功之后会提供一个密码
用户名默认为 user
Using generated security password: 9afa3ef4-035c-44bc-8dd2-c9e0e8121638
过滤链
SpringSecurity本质就时一个过滤链
在服务请求时创建,每个过滤器要放行才能去下一步
FilterSecurityInterceptor 方法级的权限过滤器,基本谓语过滤链的底部
doFilter 才是真正的过滤方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
this.invoke(new FilterInvocation(request, response, chain));
}
invoke 具体执行的方法
InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
//过滤器之前必须被放行才会继续执行下面的方面
ExceptionTranslationFilter 异常过滤器 用来处理认证授权过程中抛出的异常
doFilter中会判断你的异常 然后进行处理
UsernamePasswordAuthenticationFilter 对/login的POST请求做拦截,校验表单中的用户名,密码。
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
username = username != null ? username : "";
username = username.trim();
String password = this.obtainPassword(request);
password = password != null ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
过滤器加载过程
springboot会对springsecurity进行一个自动化的配置方案,所以我们引入依赖就可以不用额外配置。
1、使用SpringSecurity配置过滤器DelegatingFilterProxy
doFilter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
initDelegate
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = this.getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
if (this.isTargetFilterLifecycle()) {
delegate.init(this.getFilterConfig());
}
return delegate;
}
doFilter中会调用初始化方法initDelegate,他会得到一个过滤器的名字targetBeanName 会得到FilterChainProxy这个内置过滤器
//过滤链中的所以过滤器都放过来
List filters = this.getFilters((HttpServletRequest)firewallRequest);
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
List filters = this.getFilters((HttpServletRequest)firewallRequest);
if (filters != null && filters.size() != 0) {
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> {
return "Securing " + requestLine(firewallRequest);
}));
}
FilterChainProxy.VirtualFilterChain virtualFilterChain = new FilterChainProxy.VirtualFilterChain(firewallRequest, chain, filters);
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
} else {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> {
return "No security for " + requestLine(firewallRequest);
}));
}
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
}
}
private List getFilters(HttpServletRequest request) {
int count = 0;
Iterator var3 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var3.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var3.next();
if (logger.isTraceEnabled()) {
++count;
logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, count, this.filterChains.size()));
}
} while(!chain.matches(request));
return chain.getFilters();
}
FilterChainProxy里就时把所以的过滤器加载到了过滤链中
两个重要接口
用户名时固定的user,密码是自动生成的,但是我们肯定是是要改的。
1.创建一个类继承UsernamePasswordAuthenticationFilter ,然后重新里面的三个方法,attemptAuthentication,successfulAuthentication,unsuccessfulAuthentication,
2.创建类实现UserDetailsService 接口,编写查询数据库的过程,返回一个User对象,这个user是安全框架的对象,不是自己写的。
UserDetailsService
这个接口就是写去数据库查东西的过程。
PasswordEcoder
数据加密接口,用于返回的User对象里面密码的加密
设置登录的用户名和密码
第一种、通过配置文件配置
spring:
security:
user:
name: "shuaikb"
password: "shuaikb"
第二种、通过配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String password = bCryptPasswordEncoder.encode("123");
auth.inMemoryAuthentication().withUser("test").password(password).roles("admin");
}
}
//注意要去掉super.configure(auth);
第三种、自定义编写实现类
如果你在配置文件和配置类都没有配置用户和密码,系统就会去找UserDetailsService 这个接口
1.创建一个类继承UsernamePasswordAuthenticationFilter ,然后重新里面的三个方法,attemptAuthentication,successfulAuthentication,unsuccessfulAuthentication,
2.创建类实现UserDetailsService 接口,编写查询数据库的过程,返回一个User对象,这个user是安全框架的对象,不是自己写的
@Configuration
public class SecurityConfig3 extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//权限集合
List auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("shuaikb",new BCryptPasswordEncoder().encode("123"),auths);
}
}
整合数据库
mysql
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
3.4.3.1
实体类
/**
* @Author Shuaikb
* @create 2021/8/23 20:54
*/
@Data
public class Users {
private String id;
private String usernma;
private String password;
}
mapper接口
/**
* @Author Shuaikb
* @create 2021/8/23 20:56
*/
public interface UserMapper extends BaseMapper{
}
在service中去注入接口
@Autowired
private UserMapper userMapper;
具体判断代码
/**
* @Author Shuaikb
* @create 2021/8/22 23:43
*/
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名去查用户
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username",username);
Users users = userMapper.selectOne(queryWrapper);
if (users ==null){
throw new UsernameNotFoundException("用户名不存在!");
}
//权限集合
List auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User(users.getUsernma(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
注意有mapper 就要在启动类加注解
@MapperScan("com.example.springsecurity.mapper")