继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Spring Security(4)

湘王爱娟娟
关注TA
已关注
手记 101
粉丝 7
获赞 15

您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~


前面的方法中除了login()方法能成功,另外两个都失败,并不是因为代码问题,而是Spring Security默认是通过Web页面来实现页面逻辑跳转的但在前后端分离的开发模式中,页面跳转的逻辑后端已经无法直接控制了,而是通过返回状态码由前端来执行跳转因此,需要对应用进行改造

首先自定义认证成功处理器也就是实现AuthenticationSuccessHandler接口

/**
 * 自定义认证成功处理器
 *
 * @author 湘王
 */
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
   @Override
   public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                              Authentication authentication) throws IOException, ServletException {
      System.out.println("登录成功");

      // 前后端分离的调用方式
      response.setStatus(HttpStatus.OK.value());
      response.setContentType("application/json;charset=UTF-8");
      response.getWriter().write("OK");
   }
}

 

接着来实现之前没有的认证失败处理器AuthenticationFailureHandler

/**
 * 自定义认证失败处理器
 *
 * @author 湘王
 */
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
   @Override
   public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                              AuthenticationException exception) throws IOException, ServletException {
      System.out.println("user or password error");

      // 前后端分离的调用方式
      response.setStatus(HttpStatus.OK.value());
      response.setContentType("application/json;charset=UTF-8");
      response.getWriter().write("user or password error");
   }
}

 

再实现登出处理器LogoutSuccessHandler

/**
 * 自定义登出处理器
 *
 * @author 湘王
 */
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                Authentication authentication) throws IOException, ServletException {
        System.out.println("登出成功");

        // 前后端分离的调用方式
        response.setStatus(HttpStatus.OK.value());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("OK");
    }
}

 

然后修改之前的修改WebSecurityConfiguration让过滤器中增加对这几个处理器的支持

// 控制逻辑
@Override
protected void configure(HttpSecurity http) throws Exception {
   // 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤
   http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);

   http.authorizeRequests()
         .anyRequest().authenticated()
         // 设置自定义认证成功、失败及登出处理器
         .and().formLogin().loginPage("/login")
         .successHandler(successHandler).failureHandler(failureHandler).permitAll()
         .and().logout().logoutUrl("/logout").deleteCookies("JSESSIONID")
         .logoutSuccessHandler(logoutSuccessHandler).permitAll()
         // 跨域访问
         .and().cors()
         .and().csrf().disable();
}

 

现在再次测试LoginController中的登录和登出操作可以看到它们都能成功执行目前权限已经做到了与Controller中的业务逻辑无关了

 

但是仅仅做到登录登出操作肯定是不行的这连Demo都不如所以接下里来看看一个比较核心的问题:用户角色是否有效(不然建角色表干嘛)。

先在LoginController类中加入下面两个方法

@GetMapping("/admin")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String admin() {
    return "admin有ROLE_ADMIN角色";
}

@GetMapping("/manager")
@PreAuthorize("hasRole('ROLE_MANAGER')")
public String manager() {
    return "manager有ROLE_MANAGER角色";
}


启动应用运行postman时会发现

1、admin登录,访问localhost:8080/admin正常,但访问localhost:8080/manager就出现Forbidden错误

2、manager登录,访问localhost:8080/manager正常,而访问localhost:8080/admin也出现Forbidden错误

这说明用户角色权限已经起作用了但显示Forbidden的方式不够友好所以再来增加一个自定义处理器也就是实现AccessDeniedHandler接口让访问拒绝友好一些

/**
 * 自定义访问被拒绝
 *
 * @author 湘王
 */
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
   @Override
   public void handle(HttpServletRequest request, HttpServletResponse response,
                  AccessDeniedException exception) throws IOException, ServletException {
      System.out.println("permission denied");

      // 前后端分离的调用方式
      response.setStatus(HttpStatus.OK.value());
      response.setContentType("application/json;charset=UTF-8");
      response.getWriter().write("permission denied");
   }
}

 

然后同样地要在WebSecurityConfiguration过滤器链中增加这个新增的过滤器

// 控制逻辑
@Override
protected void configure(HttpSecurity http) throws Exception {
   // 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤
   http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);

   http.authorizeRequests()
      .anyRequest().authenticated()
      // 设置自定义认证成功、失败及登出处理器
      .and().formLogin().loginPage("/login")
      .successHandler(successHandler).failureHandler(failureHandler).permitAll()
      .and().logout().logoutUrl("/logout").deleteCookies("JSESSIONID")
      .logoutSuccessHandler(logoutSuccessHandler).permitAll()
      // 配置无权访问的自定义处理器
      .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler)
      .and().cors()
      .and().csrf().disable();
}

 

再用postman进行测试的时候就能看到现在的提示现在已经比刚才要友好了

 


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~



打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP