WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter 类是个适配器, 在配置的时候,需要我们根据自己的业务写个配置类去继承他

package sc.whorl.system.config.springsecurity.conf;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import sc.whorl.system.config.jwt.JwtAuthenPreFilter;
import sc.whorl.system.config.springsecurity.handler.UnauthorizedHandler;


/***
 *
 * @FileName: WebSecurityConfig
 * @remark: web 安全性配置
 * @explain 当用户登录时会进入此类的loadUserByUsername方法对用户进行验证,验证成功后会被保存在当前回话的principal对象中
 *             系统获取当前登录对象信息方法 WebUserDetails webUserDetails = (WebUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 *
 */

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        //@Secured("ROLE_ADMIN")
        securedEnabled = true,
        //@RolesAllowed("ROLE_ADMIN")
        jsr250Enabled = true,
        //@PreAuthorize("hasRole('ROLE_USER')")
        prePostEnabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    // 不需要认证的接口
    @Value("${jwt.security.antMatchers}")
    private String antMatchers;

    /**
     * 置user-detail服务
     * 方法描述
     * accountExpired(boolean)                定义账号是否已经过期
     * accountLocked(boolean)                 定义账号是否已经锁定
     * and()                                  用来连接配置
     * authorities(GrantedAuthority...)       授予某个用户一项或多项权限
     * authorities(List)                      授予某个用户一项或多项权限
     * authorities(String...)                 授予某个用户一项或多项权限
     * disabled(boolean)                      定义账号是否已被禁用
     * withUser(String)                       定义用户的用户名
     * password(String)                       定义用户的密码
     * roles(String...)                       授予某个用户一项或多项角色
     *
     * @param auth
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置UserDetailsService
        auth.userDetailsService(customUserDetailsService)
                //使用BCrypt进行密码的hash
                .passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * 声明AuthenticationManager
     *
     * @return
     */
    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager customAuthenticationManager() throws Exception {
        return authenticationManager();
    }

    /**
     * 密码加密方式
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置如何通过拦截器保护请求
     * 指定哪些请求需要认证,哪些请求不需要认证,以及所需要的权限
     * 通过调用authorizeRequests()和anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证
     *
     * 方法描述
     * anonymous()                                        允许匿名用户访问
     * authenticated()                                    允许经过认证的用户访问
     * denyAll()                                          无条件拒绝所有访问
     * fullyAuthenticated()                如果用户是完整的话(不是通过Remember-me功能认证的),就允许访问
     * hasAnyAuthority(String...)                 如果用户具备给定权限中的某一个的话,就允许访问
     * hasAnyRole(String...)                    如果用户具备给定角色中的某一个的话,就允许访问
     * hasAuthority(String)                     如果用户具备给定权限的话,就允许访问
     * hasIpAddress(String)                    如果请求来自给定IP地址的话,就允许访问
     * hasRole(String)                        如果用户具备给定角色的话,就允许访问
     * not()                               对其他访问方法的结果求反
     * permitAll()                           无条件允许访问
     * rememberMe()                          如果用户是通过Remember-me功能认证的,就允许访问
     *
     * @param http
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭csrf验证
        http.cors().and().csrf().disable()
                // 基于token,所以不需要session,此处策略为不需要创建
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //对请求进行认证  url认证配置顺序为:1.先配置放行不需要认证的 permitAll() 2.然后配置 需要特定权限的 hasRole() 3.最后配置 anyRequest().authenticated()
                .authorizeRequests().antMatchers("/",
                "/favicon.ico",
                "/**/*.png",
                "/**/*.gif",
                "/**/*.svg",
                "/**/*.jpg",
                "/**/*.html",
                "/**/*.css",
                "/**/*.js","/swagger-resources/**","/webjars/springfox-swagger-ui/**","/v2/api-docs/**")
                .permitAll()
                // 所有 antMatchers配置的 请求的都放行 不做认证即不需要登录即可访问,可以配置登陆下载等不需要token的请求路径
                .antMatchers(antMatchers.split(",")).permitAll()
                .antMatchers(HttpMethod.GET, "/api/download/**")
                .permitAll().antMatchers(HttpMethod.OPTIONS).permitAll()//跨域请求会先进行一次options请求
                // 其他请求都需要进行认证,认证通过够才能访问   待考证:如果使用重定向 httpServletRequest.getRequestDispatcher(url).forward(httpServletRequest,httpServletResponse); 重定向跳转的url不会被拦截(即在这里配置了重定向的url需要特定权限认证不起效),但是如果在Controller 方法上配置了方法级的权限则会进行拦截
                .anyRequest().authenticated()
                .and().exceptionHandling()
                // 认证配置当用户请求了一个受保护的资源,但是用户没有通过登录认证,则抛出登录认证异常,MyAuthenticationEntryPointHandler类中commence()就会调用
                .authenticationEntryPoint(myAuthenticationEntryPoint());


        // 添加JWT filter 验证其他请求的Token是否合法
        http.addFilterBefore(jwtAuthenPreFilterBean(), FilterSecurityInterceptor.class);
        // 禁用缓存
        http.headers().cacheControl();


    }


    /**
     * 登录认证异常
     *
     * @return
     */
    @Bean
    public AuthenticationEntryPoint myAuthenticationEntryPoint() {
        return new UnauthorizedHandler();
    }

    /**
     * 注册jwt 过滤器
     */
    @Bean
    public JwtAuthenPreFilter jwtAuthenPreFilterBean() throws Exception {

        return new JwtAuthenPreFilter();
    }

}