2.自定义配置类——SpringSecurity

        当前Java Web项目中主流的开发模式是前后端分离的模式,而Spring Security默认的登录是由Security框架提供的页面的表单来输入用户名、密码,且由Security框架自动处理登录流程,不适合我们前后端开发的模式,我们后端需要自己开发相关验证登录流程,我们在开发测试时需要对Security 进行初始配置!         

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;
    private static Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
    public SecurityConfiguration(){
        log.debug("加载Security配置类");
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
  
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 请求路径白名单
        String[] urls = {
                "/favicon.ico",
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/product/login"
        };

        http.cors(); // 允许跨域访问,关键点在于允许预检请求
        http.csrf().disable();//禁用防止伪造攻击
        http.authorizeRequests()//要求请求必须被授权
                .antMatchers(urls)//匹配路径
                .permitAll() // 允许访问
                .anyRequest() // 除以上配置以外的请求
                .authenticated();// 经过认证的
        http.addFilterBefore(jwtAuthorizationFilter,
                UsernamePasswordAuthenticationFilter.class);
    }

}


过滤器代码段:
 

@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    public JwtAuthorizationFilter() {
        log.debug("创建过滤器:JwtAuthorizationFilter");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        log.debug("执行JwtAuthorizationFilter");

        // 清除Security上下文中的数据
        SecurityContextHolder.clearContext();

        // 从请求头中获取JWT
        String jwt = request.getHeader("Authorization");
        log.debug("从请求头中获取JWT:{}", jwt);

        // 判断JWT数据是否基本有效
        if (!StringUtils.hasText(jwt) || jwt.length() < 80) {
            log.debug("获取到的JWT是无效的,直接放行,交由后续的组件继续处理!");
            // 过滤器链继续执行,相当于:放行
            filterChain.doFilter(request, response);
            // 返回,终止当前方法本次执行
            return;
        }

        // 设置响应结果的文档类型,主要用于处理解析JWT时的异常
        response.setContentType("application/json; charset=utf-8");

        // 尝试解析JWT
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        } catch (MalformedJwtException e) {
            log.warn("解析JWT失败:{}:{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ServiceCode.ERR_JWT_PARSE, "无法获取到有效的登录信息,请重新登录!");
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (SignatureException e) {
            log.warn("解析JWT失败:{}:{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ServiceCode.ERR_JWT_PARSE, "无法获取到有效的登录信息,请重新登录!");
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (ExpiredJwtException e) {
            log.warn("解析JWT失败:{}:{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ServiceCode.ERR_JWT_EXPIRED, "登录信息已过期,请重新登录!");
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        } catch (Throwable e) {
            log.warn("解析JWT失败:{}:{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ServiceCode.ERR_JWT_PARSE, "无法获取到有效的登录信息,请重新登录!");
            String jsonResultString = JSON.toJSONString(jsonResult);
            PrintWriter writer = response.getWriter();
            writer.println(jsonResultString);
            writer.close();
            return;
        }

        // 从JWT的解析结果中获取数据
        Long id = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        String authorityListString = claims.get("authorities", String.class);
        log.debug("从JWT中解析得到id:{}", id);
        log.debug("从JWT中解析得到username:{}", username);
        log.debug("从JWT中解析得到authorities:{}", authorityListString);

        // 准备Authentication对象,后续会将此对象封装到Security的上下文中
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(id);
        loginPrincipal.setUsername(username);
        List<SimpleGrantedAuthority> authorities = JSON.parseArray(
                authorityListString, SimpleGrantedAuthority.class);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                loginPrincipal, null, authorities);

        // 将用户信息封装到Security的上下文中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);
        log.debug("已经向Security的上下文中写入:{}", authentication);

        // 过滤器链继续执行,相当于:放行
        filterChain.doFilter(request, response);
    }

}

我们分代码段来解释其中的含义:

  @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;

        首先我们要自动装配一个JWT的认证过滤器,这个JWT就是Json Web Token,现在比较流行的给客户端做标识的技术,基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。过去我们还需要用专门的服务器存放Session,存储时间也不长久,如果用cookie呢,只能存在客户端本地,无法跨域。Token就很好的结局了这个问题,我们可以理解它为车票,我们服务端是检票口,我们只负责验证票据。
 

 private static Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
    public SecurityConfiguration(){
        log.debug("加载Security配置类");
    }

        这一段是创建本类的日志对象,并在构造方法中输出,以便于我们在控制台观察程序状态。
 

 @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

         显示配置Bean方法,交由Spring管理这个BCryptPasswordEncoder对象。这个类之前的文章有介绍认证框架SpringSecurity
 

  @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

        这个是我们继承了 WebSecurityConfigurerAdapter适配器接口之后去重写的认证方法,可以自定义自己的认证方式
 

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // 请求路径白名单
        String[] urls = {
                "/favicon.ico",
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/product/login"
        };
         http.cors(); // 允许跨域访问,关键点在于允许预检请求
        http.csrf().disable();//禁用防止伪造攻击
        http.authorizeRequests()//要求请求必须被授权
                .antMatchers(urls)//匹配路径
                .permitAll() // 允许访问
                .anyRequest() // 除以上配置以外的请求
                .authenticated();// 经过认证的
        http.addFilterBefore(jwtAuthorizationFilter,
                UsernamePasswordAuthenticationFilter.class);
    }

        这个是我们继承了 WebSecurityConfigurerAdapter适配器接口之后去重写的认证方法,可以自定义自己的认证方式。这里面的具体设置信息为
         http.cors(); // 表示允许跨域访问,关键点在于允许预检请求

解释:前端浏览器往往会发出options请求,会报cors   preflight关键字样的错误,这个就是为了解决跨域问题。
       

        http.csrf().disable();//禁用防止伪造攻击 
解释:跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法,我们往往在开发中将此验证关闭。

        http.authorizeRequests()//要求请求必须被授权
                .antMatchers(urls)//匹配路径,就是上面数组中的那些地址
                .permitAll() // 允许访问所有
                .anyRequest() // 除以上配置以外的请求
                .authenticated();// 经过认证的
   

http.addFilterBefore(jwtAuthorizationFilter,
                UsernamePasswordAuthenticationFilter.class);

在框架自带的过滤器之前加入我们自己的JWT认证过滤器。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
配置 Spring Security 4.2,需要创建一个名为 "spring-security.xml" 的配置文件,并将其放置在 src/main/resources 目录下。下面是一个基本的示例配置文件: ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <security:http auto-config="true"> <security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" /> <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <security:form-login login-page="/login" default-target-url="/welcome" authentication-failure-url="/login?error" /> <security:logout logout-success-url="/login?logout" /> </security:http> <security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="user" password="password" authorities="ROLE_USER" /> <security:user name="admin" password="password" authorities="ROLE_ADMIN" /> </security:user-service> </security:authentication-provider> </security:authentication-manager> </beans> ``` 在上面的配置中,我们定义了一个基本的安全配置。我们定义了一个 HTTP 安全配置,其中包含了拦截 URL 的规则、表单登录页面和注销功能的 URL。我们还定义了一个身份验证管理器和一个身份验证提供程序,其中包含了两个用户的用户名、密码和权限信息。 请注意,这只是一个基本的示例配置文件,您需要根据您的应用程序需求进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值