SpringSecurity初探

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")

你可能感兴趣的:(java安全)