关于java:SpringSecurity初探

41次阅读

共计 7353 个字符,预计需要花费 19 分钟才能阅读完成。

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

正文完
 0