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"; }}
运行胜利之后会提供一个明码
用户名默认为 userUsing 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
doFilterpublic 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);}initDelegateprotected 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"
第二种、通过配置类
@Configurationpublic 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是平安框架的对象,不是本人写的
@Configurationpublic 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 */@Datapublic 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")