为了账号安全,请及时绑定邮箱和手机立即绑定

【Kotlin Spring Boot 服务端开发: 问题集锦】

标签:
SpringBoot

原文标题:【Kotlin Spring Boot 服务端开发: 问题集锦】 Spring Security : 自定义AccessDeniedHandler 处理 Ajax 请求

AccessDeniedHandler 接口定义:

package org.springframework.security.web.access;import org.springframework.security.access.AccessDeniedException;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/**
 * Used by {@link ExceptionTranslationFilter} to handle an
 * <code>AccessDeniedException</code>.
 *
 * @author Ben Alex
 */public interface AccessDeniedHandler {    // ~ Methods
    // ========================================================================================================

    /**
     * Handles an access denied failure.
     *
     * @param request that resulted in an <code>AccessDeniedException</code>
     * @param response so that the user agent can be advised of the failure
     * @param accessDeniedException that caused the invocation
     *
     * @throws IOException in the event of an IOException
     * @throws ServletException in the event of a ServletException
     */
    void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException,
            ServletException;
}

Spring 的默认实现是 AccessDeniedHandlerImpl  :

package org.springframework.security.web.access;import java.io.IOException;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.http.HttpStatus;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.WebAttributes;/**
 * Base implementation of {@link AccessDeniedHandler}.
 * <p>
 * This implementation sends a 403 (SC_FORBIDDEN) HTTP error code. In addition, if an
 * {@link #errorPage} is defined, the implementation will perform a request dispatcher
 * "forward" to the specified error page view. Being a "forward", the
 * <code>SecurityContextHolder</code> will remain populated. This is of benefit if the
 * view (or a tag library or macro) wishes to access the
 * <code>SecurityContextHolder</code>. The request scope will also be populated with the
 * exception itself, available from the key {@link WebAttributes#ACCESS_DENIED_403}.
 *
 * @author Ben Alex
 */public class AccessDeniedHandlerImpl implements AccessDeniedHandler {    // ~ Static fields/initializers
    // =====================================================================================

    protected static final Log logger = LogFactory.getLog(AccessDeniedHandlerImpl.class);    // ~ Instance fields
    // ================================================================================================

    private String errorPage;    // ~ Methods
    // ========================================================================================================

    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException,
            ServletException {        if (!response.isCommitted()) {            if (errorPage != null) {                // Put exception into request scope (perhaps of use to a view)
                request.setAttribute(WebAttributes.ACCESS_DENIED_403,
                        accessDeniedException);                // Set the 403 status code.
                response.setStatus(HttpStatus.FORBIDDEN.value());                // forward to error page.
                RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
                dispatcher.forward(request, response);
            }            else {
                response.sendError(HttpStatus.FORBIDDEN.value(),
                    HttpStatus.FORBIDDEN.getReasonPhrase());
            }
        }
    }    /**
     * The error page to use. Must begin with a "/" and is interpreted relative to the
     * current context root.
     *
     * @param errorPage the dispatcher path to display
     *
     * @throws IllegalArgumentException if the argument doesn't comply with the above
     * limitations
     */
    public void setErrorPage(String errorPage) {        if ((errorPage != null) && !errorPage.startsWith("/")) {            throw new IllegalArgumentException("errorPage must begin with '/'");
        }        this.errorPage = errorPage;
    }
}

自定义实现 MyAccessDeniedHandler

package com.ksb.ksb_with_security.handlerimport org.slf4j.LoggerFactoryimport org.springframework.http.HttpStatusimport org.springframework.security.access.AccessDeniedExceptionimport org.springframework.security.web.WebAttributesimport org.springframework.security.web.access.AccessDeniedHandlerimport javax.servlet.http.HttpServletRequestimport javax.servlet.http.HttpServletResponseclass MyAccessDeniedHandler : AccessDeniedHandler {
    val logger = LoggerFactory.getLogger(MyAccessDeniedHandler::class.java)

    var errorPage: String? = null

    constructor(errorPage: String?) {        this.errorPage = errorPage
    }    override fun handle(request: HttpServletRequest, response: HttpServletResponse, accessDeniedException: AccessDeniedException) {        val isAjax = ControllerTools.isAjaxRequest(request)        if (!response.isCommitted) {            if (isAjax) {                val msg = accessDeniedException.message
                logger.info("accessDeniedException.message = $msg")                val accessDenyMsg = """
                    {
                    "code":"403",
                    "msg": "没有权限"
                    }
                """.trimIndent()

                ControllerTools.print(response, accessDenyMsg)
            } else if (errorPage != null) {                // Put exception into request scope (perhaps of use to a view)
                request.setAttribute(WebAttributes.ACCESS_DENIED_403,
                        accessDeniedException)                // Set the 403 status code.
                response.status = HttpStatus.FORBIDDEN.value()                // forward to error page.
                val dispatcher = request.getRequestDispatcher(errorPage)
                dispatcher.forward(request, response)
            }
        }
    }
}object ControllerTools {    fun isAjaxRequest(request: HttpServletRequest): Boolean {        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"), true)
    }    fun print(response: HttpServletResponse, msg: String) {
        response.characterEncoding = "UTF-8"
        response.contentType = "application/json; charset=utf-8"
        val out = response.writer        out.append(msg)        out.flush()
    }
}

然后,在自定义的继承 WebSecurityConfigurerAdapter的配置类WebSecurityConfig 中这样使用上面自定义的MyAccessDeniedHandler

package com.ksb.ksb_with_security.securityimport com.ksb.ksb_with_security.handler.MyAccessDeniedHandlerimport com.ksb.ksb_with_security.service.MyUserDetailServiceimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configurationimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilderimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurityimport org.springframework.security.config.annotation.web.builders.HttpSecurityimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurityimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapterimport org.springframework.security.core.userdetails.UserDetailsServiceimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoderimport org.springframework.security.web.access.AccessDeniedHandler/**
prePostEnabled :决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..]
secureEnabled : 决定是否Spring Security的保障注解 [@Secured] 是否可用
jsr250Enabled :决定 JSR-250 annotations 注解[@RolesAllowed..] 是否可用.
 */@Configuration@EnableWebSecurity// 开启 Spring Security 方法级安全@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)class WebSecurityConfig : WebSecurityConfigurerAdapter() {    @Bean
    fun myAccessDeniedHandler(): AccessDeniedHandler {        return MyAccessDeniedHandler("/403")
    }    @Bean
    override fun userDetailsService(): UserDetailsService {        return MyUserDetailService()
    }    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {
        http.csrf().disable()
        http.authorizeRequests()
            .antMatchers("/", // 首页不拦截
                    "/css/**",                    "/fonts/**",                    "/js/**",                    "/images/**" // 不拦截静态资源
            ).permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/login")// url 请求路径,对应 LoginController 里面的 @GetMapping("/login")
            .usernameParameter("username")
            .passwordParameter("password")
            .defaultSuccessUrl("/main").permitAll()
            .and()
            .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler())//            .exceptionHandling().accessDeniedPage("/403")
            .and()
            .logout().permitAll()

        http.logout().logoutSuccessUrl("/")

    }    @Throws(Exception::class)
    override fun configure(auth: AuthenticationManagerBuilder) {        //AuthenticationManager 使用我们的 lightSwordUserDetailService 来获取用户信息
        auth.userDetailsService(userDetailsService())
            .passwordEncoder(passwordEncoder())
    }    /**
     * 密码加密算法
     *
     * @return
     */
    @Bean
    fun passwordEncoder(): BCryptPasswordEncoder {        return BCryptPasswordEncoder();
    }
}



作者:一个会写诗的程序员
链接:https://www.jianshu.com/p/680f43c25419


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消