自定义403:
- 首先需要自定义异常
/**
* 自定义springboot错误处理异常
*/
@Configuration
public class WebSeverErrorConfig {
//出现异常以后就会访问后面指定的路径
@Bean
public ConfigurableServletWebServerFactory webServerFactory(){
TomcatServletWebServerFactory factory =new TomcatServletWebServerFactory();
ErrorPage errorPage400=new ErrorPage(HttpStatus.BAD_REQUEST,"/error/400");
ErrorPage errorPage401=new ErrorPage(HttpStatus.UNAUTHORIZED,"/error/401");
ErrorPage errorPage403=new ErrorPage(HttpStatus.FORBIDDEN,"/error/403");
ErrorPage errorPage404=new ErrorPage(HttpStatus.NOT_FOUND,"/error/404");
ErrorPage errorPage415=new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE,"/error/415");
ErrorPage errorPage500=new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/error/500");
factory.addErrorPages(errorPage400,errorPage401,errorPage403,errorPage404,errorPage415,errorPage500);
return factory;
}
}
- 然后定义一个Controller接口,对应你设置的错误路径
@ResponseBody
@RequestMapping("/error/403")
public String error403() {
return "权限不足";
}
也可以关闭body注解使用页面响应
自定义表单登录页面:
由于我们不是前后端分离的,这里我们随便准备一个简单的登录表单
<h1>登录页</h1>
<div>
<form action="/login" method="post"> <!--提交路径,提交方式-->
<div>账户:<input type="text" name="username"></div>
<div>密码:<input type="text" name="password"></div>
<input type="submit" value="登录"><!--提交按钮-->
</form>
</div>
- 注意用户名密码必须是username和password;
然后创建对应的接口
这里上面不能有@ResponsetBody或者@RestController,因为这里返回loging并不是返回login字符串,而是返回longin路径的页面;
@RequestMapping("/login")
public String login() {
return "login.html";
}
在配置类中修改http对象
- 首先放开login路径的权限拦截,否则都被拦截了从何验证
http.authorizeRequests().antMatchers("/login").permitAll()//可以直接在别的后面接续;
- 在配置完使用form表单验证的api后使用.调用
loginPage(“”)
配置验证路径并且修改.csrf().disable();
http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin()
.loginPage("/login").and().csrf().disable();
- 也就是说当访问login路径的时候就会访问到我们创建的接口并返回我们指定的登录界面,我们占用了默认的路径;然后表单提交的时候,就会提交到我们在配置类中配置的Login提交路径,会从中提取数据和内存中的用户信息进行比对;
在前后端分离中的配置
// 配置拦截规则,认证方式的方法
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO 后续可以在redis中缓存,顺便也防止别有用心之人DDOS
List<Auth> auths = authService.authFindAll();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
eac = http.authorizeRequests();
auths.forEach(auth -> {
eac.antMatchers(auth.getUri()).hasAnyAuthority(auth.getAuthTag());
});
//前后端分离中,配置的登出登入跳转路由都可以用控制器替代,也可以共存
eac.antMatchers("/user").permitAll()
.antMatchers("/**").fullyAuthenticated().and().formLogin()
/*登录*/
//处理登录请求的接口
.loginProcessingUrl("/user/login")
//接收用户名的表单名称
.usernameParameter("username")
//接收密码的表单名称
.passwordParameter("password")
//登录成功后访问的路径
.successForwardUrl("/user/loginSuccess")
//登录失败后访问的路径
.failureForwardUrl("/user/loginFailure")
.permitAll();//配置完毕登录路径一律放行
/*登出*/
//退出登录时访问的路径
http.logout().logoutUrl("/user/loginOut")
//退出后访问的路径
.logoutSuccessUrl("/user/loginOutSuccess")
.permitAll();//配置完毕,退出一律放行
/*异常处理*/
//以下异常处理配置
//未认证用户访问需要认证资源异常处理
http.exceptionHandling().authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
httpServletResponse.setContentType("application;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.print(JSON.toJSONString(new Result(false, CodeEnum.UN_LOGIN.code(), MsgEnum.UN_LOGIN.getCnMsg())));
out.flush();
})
//认证用户访问资源权限不足异常处理
.accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
httpServletResponse.setContentType("application;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.print(JSON.toJSONString(new Result(false, CodeEnum.NO_PERMISSION.code(), MsgEnum.NO_PERMISSION.getCnMsg())));
out.flush();
})
//以上异常处理配置
.and()
.cors()//允许跨域,如果springboot/springmvc已有跨域配置,自动采用springboot/springmvc跨域配置
.and()
//禁用防CSRF攻击
.csrf().disable();
}
补充:security接受账号密码的方式为formData
所以用常规post-json是无法让security接收到的;
需要使用formdata
或者直接地址传参
,可是为了保密性选择前者最好;
vue示例=>请求中加入参数:headers: { 'content-type': 'application/x-www-form-urlencoded' },
export function login(data) {
return request({
url: '/user/login',
method: 'post',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data
})
}
重要
:
关于配置的生效顺序
在这里,12可以访问,但是3无法访问;
猜测:从上到下配置规则。如果有重复的就重写之前的;
比如这样,在3被重写以后,3就可以访问了
可以的话用一句解决,里面传入数组;简单明了一点;
情况2:
1.2.3可以访问,4无法访问;
猜测:
他会像数组一样从前到后匹配。匹配得上就放行,匹配不上就直接403打回。。所以4在遇到/**以后就直接被打回了;
将all移下一行以后就会,在遇到all之前提前被放行
也就是说只要你在第一行配置了拦截所有/**,那第一个必定会碰到全部拦截。后面配置啥都没用; 同理只要你在拦截策略之前配置了放行全部。那么等于全部放行了啥都白瞎; 之前有幸配置了细化拦截的才会被拦截下来
(除非你拦截的时候把前面的/**重写了)
就像酱紫。别指望前面的没匹配上到了全部放行会放过你。因为他已经被后面的重写了;
总之他就是一个TreeMap集合,,会按照你存进去的顺序一个一个比较,但是又是一个去重集合,后面的会覆盖前面的;