SpringBoot 2整合SpringSecurity权限管理(七)实现基于Ajax的访问

前言

前面所做的SpringSecurity登录处理都是基于页面的,现在流行前后端分离开发,没有使用表单,数据交互都是使用JSON,下面来看看怎么处理Ajax请求,移动端的请求验证也适用。

一、修改原login页面的form表单

这是原来的:

<form th:action="@{/login}" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><label> code: <input type="text" name="captcha"/> </label>
        <img src="/getCode" id="code" alt="验证码" onclick="refresh(this)"/>
    </div>
      <label> <input type="hidden" name="uuid" id="uuid"/> </label>
    <div><input type="submit" value="Sign In"/></div>
</form>

我们要修改成这样:

<form >
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><label> code: <input type="text" name="captcha"/> </label> 
        <img src="/getCode" id="code" alt="验证码" onclick="refresh(this)"/>
    </div>
    <label> <input type="hidden" name="uuid" id="uuid"/> </label>
    <div><a  href="javascript:login();" >登录</a></div>
</form>
// js

    let login=()=>{
        let username = $("input[name = 'username']");
        let password = $("input[name = 'password']");
        let captcha = $("input[name = 'captcha']");
        let uuid = $("input[name = 'uuid']");
        $.ajax({
            type : "POST",
            url : "/login",
            data : {
                "username" : username.val(),
                "password" : password.val(),
                "captcha" : captcha.val(),
                "uuid" : uuid.val()
            },

            success : function(data) {
                console.log(data);
                if (data.code === 1) {
                    //登录成功
                    window.location.href = "/";
                } else {
                    alert("登录失败");
                }
            },
            error : function(data) {
                alert("错误的用户或密码");
            },
            beforeSend : function() {
                if (username.val() === "") {
                    alert("请输入用户名!");
                    username.focus();
                    return false;
                }
                if (password.val() === "") {
                    alert("请输入密码!");
                    password.focus();
                    return false;
                }
            }
        });
    };

二、在controller中新增login方法,用来代替spring security的登录界面

/**
 * 这个接口用来代替spring security的登录界面
 *
 * @Author zhangchao
 @Date 2020/3/21 02:44
     * @param
     * @retrun java.lang.Object
     **/
@GetMapping("/login_page")
public Object loginpage(){
  return new ResponseMessage(false,"0","还未登录,请登录!",null);
}

三、在Spring Security配置类SecurityConfig中加入login_page

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //需要授权的请求
                .antMatchers("/login","/login_page","/home","/getCode").permitAll() //过滤不需要认证的路径
                .antMatchers("/auth/**").permitAll() //过滤不需要认证的路径
                .anyRequest().authenticated() //对任何一个请求,都需要认证
                .and() //完成上一个配置,进行下一步配置
                //.httpBasic();
                .formLogin() //配置表单登录
                .loginPage("/login_page") //设置登录页面
                .loginProcessingUrl("/login")
                .successHandler(successHandler)  /* 设置成功处理器 */
                .failureHandler(failureHandler)  /* 设置失败处理器*/
                .and()
                .logout() //登出
                .logoutSuccessUrl("/home"); //设置退出页面
        // 添加验证码过滤器
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        // 禁用CSRF防护
        http.csrf().disable();
        http.addFilterBefore(jwtAuthorizationTokenFilter,UsernamePasswordAuthenticationFilter.class);
    }

另外还要修改登录成功处理器LoginSuccessHandler:

 /**
 * @author ZHANGCHAO
 * @date 2020/3/13 9:35
 * @since 1.0.0
 */
@Slf4j
@Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        log.info("登录成功!");
        /* 默认:会帮我们跳转到上一次请求的页面上 */
        //super.onAuthenticationSuccess(request, response, authentication);

        //生成token
        String token = jwtTokenUtil.makeToken((UserDetails) authentication.getPrincipal());

        log.info("用户 [{}] 登录成功!",authentication.getName());
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
//        writer.write("{\"status\":\"ok\",\"msg\":\"登录成功\",\"token\":\""+token+"\"}");
        writer.write(mapper.writeValueAsString(new ResponseMessage(true, "1", "操作成功!",token)));
        writer.flush();
        writer.close();
    }
}

由于配置的loginPage("/login_page")跳转的页面是json数据,不是一个页面,所以我们需要自定义一个AuthenticationEntryPoint,

四、自定义认证入口点类UnauthorizedEntryPoint,继承AuthenticationEntryPoint

/**
 * @program sweet-dream
 * @description:
 * @author: zhangchao
 * @date: 2020/03/21 03:03
 * @since: 1.0.0
 */
@Component
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        if (isAjaxRequest(request)){
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,e.getMessage());
        } else {
            response.sendRedirect("/login");
        }
    }

    private boolean isAjaxRequest(HttpServletRequest request){
        return request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").equals("XMLHttpRequest");
    }
}

AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常!配置该类能够对匿名用户进行拦截并返回对应数据。当用户访问被保护资源的时候,在过滤器中被发现是匿名用户则会由这个类进行处理。

AuthenticationEntryPoint简介

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

该接口只定义了一个方法 :

void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException;

这里参数request是遇到了认证异常authException用户请求,response是将要返回给客户的相应,方法commence实现,也就是相应的认证方案逻辑会修改response并返回给用户引导用户进入认证流程。

参考:AuthenticationEntryPoint导致登录页面异常

五、配置CRSF防护

  1. 在SpringSecurity配置类SecurityConfig中去掉禁止CRSF防护的代码,注入UnauthorizedEntryPoint

     // 禁用CSRF防护
    //  http.csrf().disable(); /* 关闭csrf,否则会拦截ajax请求 */
        http.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint); /* 用来解决匿名用户访问无权限资源时的异常 */
    
  2. 在登录页面login.html中开启CRSF防护,crsf开启,会拦截ajax请求所以需要在请求中加入crsf参数

    head里加:

    <head>
         <meta name="_csrf" th:content="${_csrf.token}"/>
       <meta name="_csrf_header"  th:content="${_csrf.parameterName}"/>
        <title>Spring Security Example </title>
        <script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script>
    </head>
    

    js里加:

    $.ajax({
      type : "POST",
      url : "/login",
      data : {
        "username" : username.val(),
        "password" : password.val(),
        "captcha" : captcha.val(),
        "uuid" : uuid.val(),
        [[${_csrf.parameterName}]]: [[${_csrf.token}]]
    },
    

六、启动项目测试

image-20200321175203547

登录成功!

以上

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值