前几篇,增加了对登录失败、成功的结果处理,在上一篇《基于 Spring Boot 的 SSM 环境整合十七:升级 Spring Boot 到 2.0遇到的问题》中解决了FreeMarker模板提示警告的问题。
我将相关的类进行了重命名,主要是为了规范。最后的WebSecurityConfig类如下:
package com.whowii.website4.config;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.whowii.website4.security.ExitSuccessHandler;
import com.whowii.website4.security.LoginFailHander;
import com.whowii.website4.security.LoginProvider;
import com.whowii.website4.security.LoginSuccessHandler;
import com.whowii.website4.security.LoginedDeniedHandler;
import com.whowii.website4.security.NotLoginDeniedHandler;
import freemarker.ext.jsp.TaglibFactory;
/**
* 登录成功处理
*
* @ClassName: WebSecurityConfig
* @Description: TODO
* @author:calvin
* @date 2019年1月16日 下午8:47:34
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginProvider loginProvider;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailHander loginFailHander;
@Autowired
private FreeMarkerConfigurer templetConfigurer;
@PostConstruct
public void freeMarkerConfigurer() {
List<String> tlds = new ArrayList<String>();
TaglibFactory taglibFactory = templetConfigurer.getTaglibFactory();
taglibFactory.setClasspathTlds(tlds);
if (taglibFactory.getObjectWrapper() == null) {
taglibFactory.setObjectWrapper(templetConfigurer.getConfiguration().getObjectWrapper());
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(loginProvider);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resource/**");
web.ignoring().antMatchers("/script/**");
web.ignoring().antMatchers("/ui/**");
web.ignoring().antMatchers("/favicon.ico");
}
protected void configure(HttpSecurity http) throws Exception {
System.out.println("4>>>>>>>>>>>>>SecurityConfig.configure > http");
http
.csrf().disable()
.headers()
.frameOptions() // 解决IFrame拒绝的问题
.disable()
.and()
.authorizeRequests()
.antMatchers("/manage/demo/hello", "/manage/demo/index").permitAll() // 此页面允许任何人访问,即使未登录
.antMatchers("/manage/demo/info1").hasAnyRole("ADMIN") // 仅允许 ADMIN 角色的用户访问
.antMatchers("/manage/demo/info2").hasAnyRole("USER") // 仅允许 USER 角色的用户访问
.anyRequest().denyAll() // 其他资源禁止访问
.and()
.formLogin()
.loginPage("/manage/demo/login") // 自定义登录页面
.successHandler(loginSuccessHandler) // 登录成功
.failureHandler(loginFailHander) // 登录失败
.permitAll() // 允许任何用户访问
.and()
.logout()
.logoutUrl("/manage/demo/exit") // 退出登录
.logoutSuccessHandler(new ExitSuccessHandler()) // 退出登录处理
// .logoutSuccessUrl("/manage/demo/index") // 退出登录成功返回的页面
.permitAll(); // 允许其他用户访问
}
}
本篇主要解决两个问题:一是未登录用户访问需授权资源时的结果定义,二是已登录用户访问无权限资源时的结果定义。
1、实现 AuthenticationEntryPoint 接口
package com.whowii.website4.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* 未登录无权访问处理
*
* @ClassName: LoginEntryPoint
* @Description: TODO
* @author:calvin
* @date 2019年1月16日 下午8:49:04
*/
public class NotLoginDeniedHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException,
ServletException {
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.getWriter().print("{\"success\":false,\"code\":220,\"message\":\"访问失败:" + exception.getMessage() + "\"}");
response.getWriter().flush();
}
}
此类用于处理未登录用户访问需要权限的资源时,如何返回结果。
2、实现 AccessDeniedHandler 接口
package com.whowii.website4.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
/**
* 登录无权访问处理
*
* @ClassName: LoginDeniedHandler
* @Description: TODO
* @author:calvin
* @date 2019年1月16日 下午8:48:48
*/
public class LoginedDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException,
ServletException {
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.getWriter().print("{\"success\":false,\"code\":210,\"message\":\"访问失败:" + exception.getMessage() + "\"}");
response.getWriter().flush();
}
}
此类用于处理已登录用户访问无权限的资源时,如何返回结果。
3、注册处理对象
修改WebSecurityConfig类,注册两个处理对象,完成后的代码如下:
package com.whowii.website4.config;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.whowii.website4.security.ExitSuccessHandler;
import com.whowii.website4.security.LoginFailHander;
import com.whowii.website4.security.LoginProvider;
import com.whowii.website4.security.LoginSuccessHandler;
import com.whowii.website4.security.LoginedDeniedHandler;
import com.whowii.website4.security.NotLoginDeniedHandler;
import freemarker.ext.jsp.TaglibFactory;
/**
* 登录成功处理
*
* @ClassName: WebSecurityConfig
* @Description: TODO
* @author:calvin
* @date 2019年1月16日 下午8:47:34
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginProvider loginProvider;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailHander loginFailHander;
@Autowired
private FreeMarkerConfigurer templetConfigurer;
@PostConstruct
public void freeMarkerConfigurer() {
List<String> tlds = new ArrayList<String>();
TaglibFactory taglibFactory = templetConfigurer.getTaglibFactory();
taglibFactory.setClasspathTlds(tlds);
if (taglibFactory.getObjectWrapper() == null) {
taglibFactory.setObjectWrapper(templetConfigurer.getConfiguration().getObjectWrapper());
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(loginProvider);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resource/**");
web.ignoring().antMatchers("/script/**");
web.ignoring().antMatchers("/ui/**");
web.ignoring().antMatchers("/favicon.ico");
}
protected void configure(HttpSecurity http) throws Exception {
System.out.println("4>>>>>>>>>>>>>SecurityConfig.configure > http");
http
.csrf().disable()
.headers()
.frameOptions() // 解决IFrame拒绝的问题
.disable()
.and()
.authorizeRequests()
.antMatchers("/manage/demo/hello", "/manage/demo/index").permitAll() // 此页面允许任何人访问,即使未登录
.antMatchers("/manage/demo/info1").hasAnyRole("ADMIN") // 仅允许 ADMIN 角色的用户访问
.antMatchers("/manage/demo/info2").hasAnyRole("USER") // 仅允许 USER 角色的用户访问
.anyRequest().denyAll() // 其他资源禁止访问
.and()
.formLogin()
.loginPage("/manage/demo/login") // 自定义登录页面
.successHandler(loginSuccessHandler) // 登录成功
.failureHandler(loginFailHander) // 登录失败
.permitAll() // 允许任何用户访问
.and()
.logout()
.logoutUrl("/manage/demo/exit") // 退出登录
.logoutSuccessHandler(new ExitSuccessHandler()) // 退出登录处理
// .logoutSuccessUrl("/manage/demo/index") // 退出登录成功返回的页面
.permitAll() // 允许其他用户访问
.and()
.exceptionHandling()
.accessDeniedHandler(new LoginedDeniedHandler()) // 当登录后访问无权资源时(默认跳转到报错页面)
.authenticationEntryPoint(new NotLoginDeniedHandler()); // 当未登录访问资源时(默认是跳转到登录页面)
}
}
4、测试结果
启动应用,当未登录时访问 http://127.0.0.1:8080/manage/demo/info1 结果如下:
使用 admin 登录后,访问 http://127.0.0.1:8080/manage/demo/info2 (需 USER 角色)结果如下: