Spring Security相关知识

##Spring Security配置

继承 WebSecurityConfigurerAdapter ,重写configure(HttpSecurity http)配置相关权限以及重写拦截器

WebSecurityConfigurerAdapter 类是个适配器, 在配置的时候,需要我们自己写个配置类去继承他,然后编写自己所特殊需要的配置

/**
 * 对SpringSecurity的配置的扩展,支持自定义白名单资源路径和查询用户逻辑
 */
public class SecurityConfig extends WebSecurityConfigurerAdapter {
WebSecurityConfigurerAdapter是Spring Security Config内置提供的一个WebSecurityConfigurer抽象实现类。
 

 指定了任何用户都可以访问的多个URL模式。

ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        for (String url : ignoreUrlsConfig().getUrls()) {
            registry.antMatchers(url).permitAll();
        }

允许跨域请求的OPTIONS请求
registry.antMatchers(HttpMethod.OPTIONS).permitAll();
跨域请求中,options请求是浏览器自发起的preflight request(预检请求),以检测实际请求是否可以被浏览器接受。
preflight request请求报文中有两个需要关注的首部字段:
(1)Access-Control-Request-Method:告知服务器实际请求所使用的HTTP方法;
(2)Access-Control-Request-Headers:告知服务器实际请求所携带的自定义首部字段。
同时服务器也会添加origin header,告知服务器实际请求的客户端的地址。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。

服务器所返回的Access-Control-Allow-Methods首部字段将所有允许的请求方法告知客户端,返回将所有Access-Control-Request-Headers首部字段将所有允许的自定义首部字段告知客户端。
此外,服务器端可返回Access-Control-Max-Age首部字段,允许浏览器在指定时间内,无需再发送预检请求,直接用本次结果即可。

任何请求需要身份认证
registry.and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()

关闭跨站请求防护及不使用session
.csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
 
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。
CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

从Spring Security 4开始,默认启用CSRF机制,本来这也不算什么大事,但与Spring Boot结合在一起,那么实现起来就比较麻烦了,
尤其是采用前后端分离式的开发架构后,配置CSRF机制就更困难了,几乎所有网上的解决办法都无法解决如何获取CSRF编码的难题

自定义权限拒绝处理类
exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())
                .authenticationEntryPoint(restAuthenticationEntryPoint())
/**
 * 当访问接口没有权限时,自定义的返回结果
 */
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
        response.getWriter().flush();
    }
}
在Spring默认的AccessDeniedHandler中只有对页面请求的处理。当捕获AccessDeniedException时需要给接口响应,
所以我们可以通过自定义AccessDeniedHandler来处理响应结果。


/**
 * 当未登录或者token失效访问接口时,自定义的返回结果
 */
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
        response.getWriter().flush();
    }
}

AuthenticationEntryPoint是Spring Security Web一个概念模型接口,顾名思义,他所建模的概念是:“认证入口点”。
它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程。

request是遇到了认证异常authException用户请求,response是将要返回给用户的响应。

 

 
自定义权限拦截器JWT过滤器
.addFilterBefore(jwtAuthenticationTokenFilter(),UsernamePasswordAuthenticationFilter.class);
在指定的UsernamePasswordAuthenticationFilter之前添加JwtAuthenticationTokenFilter过滤器
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
OncePerRequestFilter,他能够确保在一次请求只通过一次filter,而不需要重复执行。
OncePerRequestFilter,旨在确保每个请求调度在任何servlet容器上执行一次,它提供了一个带有HttpServletRequest和HttpServletResponse参数的方法。
 
自定义 UsernamePasswordAuthenticationFilter 实现自动登陆
//1.username和password被获得后封装到一个UsernamePasswordAuthenticationToken(Authentication接口的实例)
//2.这个token被传递给AuthenticationManager进行验证
//3.成功认证后AuthenticationManager将返回一个得到完整填充的Authentication实例
//4.通过调用SecurityContextHolder.getContext().setAuthentication(...),参数传递authentication对象,来建立安全上下文(security context)
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            //用于在Spring Security登录过程中对用户的登录信息的详细信息进行填充 authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication);
 
 
##启用Spring Security配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DemoSecurityConfig extends SecurityConfig {

    @Autowired
    private ITUserService userService;

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        //获取登录用户信息
        return username -> userService.loadUserByUsername(username);
    }

}

@Configuration注解表明这个类是一个配置类。作用为:配置spring容器(应用上下文),用来将带有@Bean的实体进行实例化bean等。

@EnableWebSecuritySpring Security用于启用Web安全的注解。典型的用法是该注解用在某个Web安全配置类上(实现了接口WebSecurityConfigurer或者继承自WebSecurityConfigurerAdapter)。

这个注解导入了@EnableGlobalAuthentication注解,通过使用这个注解启用全局认证机制。

Spring Security依赖于全局认证机制,所以这里启用全局认证机制是很自然的事。
注解@EnableGlobalAuthentication又导入了AuthenticationConfiguration用于全局认证机制配置;
AuthenticationConfiguration主要目的用于配置认证管理器组件AuthenticationManager。
AuthenticationManager会在运行时用于认证请求者身份。

@EnableGlobalMethodSecurity(prePostEnabled=true) 开启基于方法的安全认证机制,也就是说在web层的controller启用注解机制的安全确认

@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值
    @ApiOperation("创建商品")
     @RequestMapping(value = "/create", method = RequestMethod.POST)
     @ResponseBody
     @PreAuthorize("hasAuthority('pms:product:create')")
    public CommonResult create(@RequestBody PmsProductParam productParam, BindingResult bindingResult) {
        int count = productService.create(productParam);
        if (count > 0) {
            return CommonResult.success(count);
        } else {
            return CommonResult.failed();
        }
    } 
 

猜你喜欢

转载自www.cnblogs.com/lushichao/p/12503780.html