stater
// 依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
写一个 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<Filter> 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<Filter> 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<Filter> 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<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("shuaikb",new BCryptPasswordEncoder().encode("123"),auths);
}
}
整合数据库
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
实体类
/**
* @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<Users>{
}
在 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<Users> queryWrapper = new QueryWrapper();
queryWrapper.eq("username",username);
Users users = userMapper.selectOne(queryWrapper);
if (users ==null){throw new UsernameNotFoundException("用户名不存在!");
}
// 权限汇合
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User(users.getUsernma(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
留神有 mapper 就要在启动类加注解
@MapperScan("com.example.springsecurity.mapper")