目录

SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

SpringSecurity权限管理系统实战—二、日志、接口文档等实现

SpringSecurity权限管理系统实战—三、主要页面及接口实现

SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

SpringSecurity权限管理系统实战—七、处理一些问题

SpringSecurity权限管理系统实战—八、AOP 记录用户日志、异常日志

前言

在写完上一篇文章之后,我又研究了很久。最终我发现似乎我们这个项目不太适合用jwt。layui不像vue那样可以通过axios 全局设置token(或许是我因为我菜,不知道怎么设置,如果有小伙伴有好的办法,欢迎留言告诉我)。

这里稍微介绍下前端怎么操作(vue为例),主要就是拿到token以后将其存储在localstorage或者cookies中,再从localstorage或者cookies中拿到token设置全局的请求头,就可以了。

但是前一篇文章关于jwt的内容是没有问题的,正常的使用也是那样的步骤。

具体内容

去除JWT

那么既然不打算再用jwt了,就要老老实实的回去用cookies和session。那么我们需要把我们的项目还原成实战五结束时候的样子。

这里不是很好解释了,就是把与jwt相关的删除就行了,需要修改的地方有MyAuthenticationSuccessHandler,JwtAuthenticationTokenFilter,SpringSecurityConfig三处,删去有关jwt的内容即可。(不多费篇幅)

前端提示信息

我这里修改了一下登录页面,让其能够在登录失败时,给出提示信息

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="/PearAdmin/admin/css/pearForm.css" />
<link rel="stylesheet" href="/PearAdmin/component/layui/css/layui.css" />
<link rel="stylesheet" href="/PearAdmin/admin/css/pearButton.css" />
<link rel="stylesheet" href="/PearAdmin/assets/login.css" />
</head>
<body background="PearAdmin/admin/images/background.svg" >
<form class="layui-form" method="get">
<div class="layui-form-item">
<img class="logo" src="PearAdmin/admin/images/logo.png" />
<div class="title">M-S-P Admin</div>
<div class="desc">
Spring Security 权 限 管 理 系 统 实 战
</div>
</div>
<div class="layui-form-item">
<input id="username" name="username" placeholder="用 户 名 : " type="text" hover class="layui-input" required lay-verify="username"/>
</div>
<div class="layui-form-item">
<input id="password" name="password" placeholder="密 码 : " type="password" hover class="layui-input" required lay-verify="password"/>
</div>
<div class="layui-form-item">
<input id="captcha" name="captcha" placeholder="验 证 码:" type="text" hover class="layui-verify" style="border: 1px solid #dcdfe6;" required lay-verify="captcha">
<img id="captchaImg" src="/captcha" width="130px" height="44px" onclick="this.src=this.src+'?'+Math.random()" title="点击刷新"/>
</div> <div class="layui-form-item">
<input type="checkbox" id="rememberme" name="rememberme" title="记住密码" lay-skin="primary" checked>
</div>
<div class="layui-form-item">
<button style="background-color: #5FB878!important;" class="pear-btn pear-btn-primary login" lay-submit lay-filter="formLogin">
登 入
</button>
</div>
</form>
<script src="/PearAdmin/component/layui/layui.js" charset="utf-8"></script>
<script>
layui.use(['form', 'element','jquery'], function() {
var form = layui.form;
var element = layui.element;
var $ = layui.jquery;
// $("body").on("click",".login",function(obj){
// location.href="/api/admin"
// })
form.verify({
username: function(value) {
if (value.length <= 0 ) {
return '用户名不能为空';
}
},
password: function (value) {
if (value.length <= 0) {
return '密码不能为空';
}
},
captcha: function (value) {
if (value.length <= 0) {
return '验证码不能为空';
}
if (value.length !== 4) {
return '请输入正确格式的验证码';
}
}
})
form.on('submit(formLogin)', function() {
$.ajax({
url:'/login',
type:'post',
dataType:'text',
data:{
username:$('#username').val(),
password:$('#password').val(),
captcha:$('#captcha').val(),
rememberme:$('#rememberme').val()
},
success:function(result){
var restjson = JSON.parse(result)
if (restjson.success) {
// layui.data("token", {
// key: "Authorization",
// value: "Bearer "+ restjson.jwt
// });
layer.msg(restjson.msg,{icon:1,time:1000},function () {
location.href = "/"; });
}else {
layer.msg(restjson.msg,{icon:2,time:1000},function () {
$("#captchaImg").attr("src","/captcha" + "?" + Math.random());
});
return false;
}
}
})
return false;
});
})
</script>
</body>
</html>

后端也做了登录失败的处理器

/**
* @author codermy
* @createTime 2020/8/2
*/
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("utf-8");//修改编码格式
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().write(JSON.toJSONString(Result.error().message(e.getMessage())));//返回信息
}
}

AuthenticationFailureHandler是一个抽象的异常类,他的常见子类为

UsernameNotFoundException 用户找不到
BadCredentialsException 坏的凭据
AccountStatusException 用户状态异常它包含如下子类
AccountExpiredException 账户过期
LockedException 账户锁定
DisabledException 账户不可用
CredentialsExpiredException 证书过期

都是在用户登录时可能会遇到的异常

修改后完整的SpringSecurityConfig

/**
* @author codermy
* @createTime 2020/7/15
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private VerifyCodeFilter verifyCodeFilter;//验证码拦截器
@Autowired
MyAuthenticationSuccessHandler authenticationSuccessHandler;//登录成功逻辑
@Autowired
private MyAuthenticationFailureHandler authenticationFailureHandler;//登录失败逻辑
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//jwt拦截器
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;//无权限拦截器
@Autowired
private RestfulAccessDeniedHandler accessDeniedHandler;// 无权访问 JSON 格式的数据 @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.GET,
"/swagger-resources/**",
"/PearAdmin/**",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-ui.html",
"/webjars/**",
"/v2/**");//放行静态资源
} /**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
http.csrf().disable()//关闭csrf
// .sessionManagement()// 基于token,所以不需要session
// .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .and()
.httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint)//未登陆时返回 JSON 格式的数据给前端
.and()
.authorizeRequests()
.antMatchers("/captcha").permitAll()//任何人都能访问这个请求
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")//登录页面 不设限访问
.loginProcessingUrl("/login")//拦截的请求
.successHandler(authenticationSuccessHandler) // 登录成功
.failureHandler(authenticationFailureHandler) // 登录失败
.permitAll()
.and()
.rememberMe().rememberMeParameter("rememberme")
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
.and(); // 禁用缓存
http.headers().cacheControl(); // 添加JWT拦截器
// http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问返回JSON 格式的数据
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
} /**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
} }

遇到的问题

一、Springsecurity中的UsernameNotFoundException异常无法被正常捕获

具体的解释可以看这篇文章(非常详细,包括解决方案)

简而言之,就是我抛出了UsernameNotFoundException异常但是最后会被转换为BadCredentialsException异常。我这里不多做介绍了,上面那篇文章说的非常详细。

如何解决也请参照那篇文章。我所使用的是取巧的方法,就是直接抛出BadCredentialsException异常而不是UsernameNotFoundException异常。因为毕竟最后给出的提示信息是模糊的“用户名或密码错误”,而不是具体到哪个错误了。

二、无法统一处理filter中抛出的异常

这个问题主要是和验证码的拦截器有关,前端拿不到验证码错误的提示信息。这里我们可以不用拦截器来处理验证码,可以自定义一个login请求来避开这个问题。

这个问题也是原本的写法问题吧,其实原本需要用抛这个异常,直接向页面输出提示信息就好了。

我在找处理方法时找到有两种方法供大家参考

后叙

这篇文章有点乱,博主的文笔真的不太行,所以在描述一些问题的时候可能会有点难以理解。如果小伙伴们在学习过程中有什么问题,欢迎大家加我的qq(在我的码云主页有)我们一起探讨学习。

下一篇文章我们实现用户的操作日志和异常日志功能

giteegithub中可获取源代码,与本系列文章同步更新

SpringSecurity权限管理系统实战—七、处理一些问题的更多相关文章

  1. SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  2. SpringSecurity权限管理系统实战—二、日志、接口文档等实现

    系列目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战 ...

  3. SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  4. SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  5. SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  6. SpringSecurity权限管理系统实战—九、数据权限的配置

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  7. SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

    系列目录 前言 上篇文章SpringSecurity整合了一半,这次把另一半整完,所以本篇的序号接着上一篇. 七.自定义用户信息 前面我们登录都是用的指定的用户名和密码或者是springsecurit ...

  8. SpringSecurity权限管理系统实战—三、主要页面及接口实现

    系列目录 前言 后端五分钟,前端半小时.. 每次写js都头疼. 自己写前端是不可能的,这辈子不可能自己写前端的,只能找找别人的模板才能维持的了生存这样子.github,gitee上的模板又多,帮助文档 ...

  9. ABP+AdminLTE+Bootstrap Table权限管理系统第七节--登录逻辑及abp封装的Javascript函数库

    经过前几节,我们已经解决数据库,模型,DTO,控制器和注入等问题.那么再来看一下登录逻辑.这里算是前面几节的一个初次试水. 首先我们数据库已经有的相应的数据. 模型和DTO已经建好,所以我们直接在服务 ...

随机推荐

  1. spring学习(五)详细介绍AOP

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待 它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封 ...

  2. MySQL 8.0.20 安装教程图文详解(windows 64位)

    MySQL 8.0.20 安装教程图文详解(windows 64位)  更新时间:2020年05月09日 15:09:04   转载 作者:瘦肉粥不加糖     这篇文章主要介绍了MySQL 8.0. ...

  3. 4. JSON字符串是如何被解析的?JsonParser了解一下

    公司不是你家,领导不是你妈.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BA ...

  4. 02_Linux实操篇

    第五章 VI和VIM编辑器 5.1. VI和VIM基本介绍 Vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器.由于对Unix及Linux系统的任何版本,Vi ...

  5. C/C++编程笔记:C语言预处理命令是什么?不要以为你直接写#就行!

    很多小伙伴在自己写代码的时候,已经多次使用过#include命令.使用库函数之前,应该用#include引入对应的头文件.其实这种以#号开头的命令称为预处理命令. C语言源文件要经过编译.链接才能生成 ...

  6. luogu P3412 仓鼠找sugar II 期望 树形dp

    LINK:仓鼠找sugar II 以前做过类似的期望题目 加上最后的树形dp不算太难 还是可以推出来的. 容易发现 当固定起点和终点的时候 可以先固定根 这样就不用分到底是正着走还是倒着走了. 1为根 ...

  7. C语言中的数据转换和定义常量

    一.数据转换 1.数据类型转换:C 语言中如果一个表达式中含有不同类型的常量和变量,在计算时,会将它们自动转换为同一种类型:在 C 语言中也可以对数据类型进行强制转换: 2.自动转换规则: a)浮点数 ...

  8. 卷积神经网络 part1

    [任务一]视频学习心得及问题总结 根据下面三个视频的学习内容,写一个总结,最后列出没有学明白的问题. [任务二]代码练习 在谷歌 Colab 上完成代码练习,关键步骤截图,并附一些自己的想法和解读. ...

  9. tensorflow2.0 实现gpu和cpu切换

      昨天把GPU版本的tf2.0 安装成功之后,现在所有的代码运行居然都在gpu上跑了,并且在对gpu使用情况没有限制的条件下,既然gpu内存跑满了,代码就崩了怎么样才能随心所欲的指定代码是在cpu还 ...

  10. 简单的 vector

    #pragma once #include <memory.h> #include <stdlib.h> #include <iostream> using std ...