【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 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦