IDEA-Spring Security安全管理-自定义用户授权管理

当一个系统建立之后,通常需要适当地做一些权限控制,使得不同用户具有不同的权限操作系统。

用户自定义访问控制

  • 实际生产中,网站访问多是基于HTTP请求的,我们可以通过重写WebSecurityConfigurerAdapter类的configure(HTTPSecurity http)方法来对基于Http的请求访问进行控制。
  • configure(HttpSecurity http)方法的参数类型是HttpSecurity类,该类提供了Http请求的限制以及权限、Session管理配置、CSRF跨站请求问题。
authorizeRequest()开启基于HttpServletRequest请求访问的控制
formLgoin()开启基于表单的用户登录
httpBasic()开启基于HTTP请求的Basic认证登录
logout()开启退出登录的支持
sessionManagement()开启Session管理配置
rememberMe()开启记住我功能
csrf()配置CSRF跨站请求伪造防护功能
  • 自定义用户访问控制
//在SecurityConfig中重写configure(HttpSecurity http)方法进行用户访问控制
//此时我们还没有配置完善的用户注销功能,切换用户的话需要重启浏览器
@Override
    protected void configure(HttpSecurity http) throws Exception {
        //对“/”路径请求直接放行
        //"/detail/common/**"路径请求只有用户角色为common才允许访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/detail/common/**").hasRole("common")
                .antMatchers("/detail/vip/**").hasRole("vip")
                .and()
                .formLogin();
    }
  • 测试,登录首页,进入common和vip页面会提示登录,根据权限控制访问部分。

自定义用户登录

  • 自定义用户登录界面
    • 有需要资源的老哥老弟可以私聊我
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户登录界面</title>
<!--    连接静态资源文件-->
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<!--    通过<form>标签定义了一个用户登录功能,且登录数据以POST方式通过/userLogin路径提交-->
    <form class="form-signin" th:action="@{/userLogin}" th:method="post" >
        <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72px" height="72px">
        <h1 class="h3 mb-3 font-weight-normal">请登录</h1>
        <!-- 用户登录错误信息提示框 -->
<!--        判断请求参数param为Security默认的-->
        <div th:if="${param.error}" style="color: red;height: 40px;text-align: left;font-size: 1.1em">
            <img th:src="@{/login/img/loginError.jpg}" width="20px">用户名或密码错误,请重新登录!
        </div>
        <input type="text" name="name" class="form-control" placeholder="用户名" required="" autofocus="">
        <input type="password" name="pwd" class="form-control" placeholder="密码" required="">
        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" name="rememberme"> 记住我
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button>
        <p class="mt-5 mb-3 text-muted">Copyright© 2019-2020</p>
    </form>
</body>
</html>
  • 自定义用户登录跳转
// Spring Security默认采用Get方式的“/login”请求用于向登录页面跳转,使用Post方式的"/login"请求用于对登录后的数据处理
    @GetMapping("/userLogin")
    public String toLoginPage(){
        return "login";
    }
  • 自定义用户登录控制
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                //对登陆页面放行
                .antMatchers("login").permitAll()
                //对static文件夹下静态资源进行统一放行
                .antMatchers("/login/**").permitAll()
                .antMatchers("/detail/common/**").hasRole("common")
                .antMatchers("/detail/vip/**").hasRole("vip")
                //匹配任何请求-已经登录的用户
                .anyRequest().authenticated();
        // 自定义用户登录控制
        http.formLogin()
                //指定想自定义登录页跳转的请求路径并放行
                .loginPage("/userLogin").permitAll()
                //接受登录的账号密码
                .usernameParameter("name").passwordParameter("pwd")
                // 默认成功界面
                .defaultSuccessUrl("/")
                // 失败页面
                .failureUrl("/userLogin?error");
    }
  • 效果测试,发现登录页面已经被Security拦截并跳转到自定义的用户登录页面login.html.

自定义用户退出

  • 自定义用户退出主要考虑退出后的会话如何管理以及跳转到哪个页面。HttpSecurity类的logout()方法用来处理用户退出,它默认处理路径为"/logout"的Post类型请求,同时也会清除Session和“Remember Me”等任何默认用户配置。
  • 添加自定义用户退出链接
    • 需要注意的是,springboot引入spring security框架后会自动开启CSRF防护功能(跨站请求伪造防护),用户退出时必须使用POST请求;如果关闭了CSRF防护功能,那么就可以使用任意方式的HTTP请求进行用户注销。
<!--在index.html中新增一个<form>标签进行注销控制,且定义的退出表单action为"/mylogout"(默认为"/logout"),方法为post-->
<form th:action="@{/mylogout}" method="post">
	<input th:type="submit" th:value="注销">
</form>
  • 自定义用户退出控制
    • 在页面中定义好用户退出链接时,不需要再Controller控制层中额外定义用户退出方法,可以直接在Security中定制logout()方法实现用户退出。
  // 自定义用户退出控制
        http.logout()
                //指定用户退出的请求路径,该路径需要与index.html界面退出表单的action值保持一致
                .logoutUrl("/mylogout")
                //退出成功重定义
                .logoutSuccessUrl("/");
  • 效果测试,无须重启浏览器进行切换用户

登录用户信息获取

  • 传统项目中进行用户登录处理时,通常会查询用户是否存在,如果存在则登录成功,同时将当前用户放在Session中。
  • 两种方式获取登录后的用户信息:HttpSession和SecurityContextHolder方式
  • HttpSession方式
    • 对于该方式,测试时登录用户后,再使用同一浏览器访问getUserBySession可以在控制台看到输出信息
@GetMapping("/getUserBySession")
    @ResponseBody
    public void getUser(HttpSession session){
        // 从当前HttpSession获取绑定到此会话的所有对象的名称
        Enumeration<String>  names = session.getAttributeNames();
        while(names.hasMoreElements()){
            // 获取HttpSession中会话名称
            String element = names.nextElement();
            // 获取HttpSession中的应用上下文
            SecurityContextImpl attribute =(SecurityContextImpl)session.getAttribute(element);
            System.out.println("element:"+element);
            System.out.println("attribute:"+attribute);
            // 获取用户相关消息
            Authentication authentication = attribute.getAuthentication();
            UserDetails principal = (UserDetails)authentication.getPrincipal();
            System.out.println(principal);
            System.out.println("username:"+principal.getUsername());
        }
    }
  • 使用SecurityContextHolder获取用户信息
    • Spring Security针对拦截的登录用户专门提供了一个SecurityContextHolder类,来获取应用上下文SecurityContex,进而获取封装的用户信息。
    • 测试方式如上Session方式
    • HttpSession方式获取用户信息相对比较传统,而且必须引入HttpSession对象;而Security提供的SecurityContextHolder相对简便,也是Security项目相对推荐的使用方式。
    @GetMapping("getUserByContext")
    @ResponseBody
    public void getUser2(){
        //获取应用上下文
        SecurityContext context = SecurityContextHolder.getContext();
        System.out.println("userDetails:"+context);
        //获取用户相关信息
        Authentication authentication = context.getAuthentication();
        UserDetails principal = (UserDetails)authentication.getPrincipal();
        System.out.println(principal);
        System.out.println("username:"+principal.getUsername());
    }

记住我功能

  • SpringSecurity针对记住我功能提供了两种实现:
    • 一种是简单地使用加密来保证基于Cookie中Token的安全
    • 一种是通过数据库或其他持久化机制来保存生成的Token
  • 基于简单加密Token的方式
    • 当用户选择记住我并成功登录后,SpringSecurity会生成一个Cookie并发送给客户端浏览器。
    • Cookie值的组合加密方式: base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key)
    • 安全隐患:任何人获取到该记住我功能的Token后,都可以在该Token值过期之前进行自动登录,只有当用户觉察到Token呗盗用后,才会对自己的登录密码进行修改来立即使其原有的记住我Token失效
    • 效果测试:勾选了记住我选项后,在有效期内再次访问不需要重新登录验证。
 // 定制Remember-me记住我功能
        http.rememberMe()
                .rememberMeParameter("rememberme")
                // 有效时间200s
                .tokenValiditySeconds(200);
  • 基于持久化Token的方式
    • 区别:基于简单加密Token的方式,生成的Token将在客户端保存一段时间,如果用户不退出登录,或者不修改密码,那么在Cookie失效之前,任何人都可以无限制地使用该Token进行自动登录。
    • 基于持久化Token的方式采用如下逻辑:
      • 用户选择记住我成功登录后,Security会把username、随机产生的序列号、生成的Token进行持久化存储,同时将它们的组合生成一个Cookie发送给客户端浏览器。
      • 当用户再次访问系统时,首先检查客户端携带的Cookie,如果定义保存的一致,则通过验证并自动登录,同时系统将重新生成一个新的Token替换旧的Token,并把新的Token再次发送给客户端
      • 如果Cookie中的Token不匹配,就需要重新登录,生成新的Token和Cookie,此时有可能Cookie被盗用了。
      • 如果用户访问系统时没有携带Cookie,或者包含的username和序列号与数据库中保存的不一致,则会引导用户到登录页面。
    • 持久化Token的方式比简单加密Token的方式相对更加安全。
  • 创建存储Cookie信息的用户表
    在这里插入图片描述
  • 编写持久化存储的代码
		...
        http.rememberMe()
                .rememberMeParameter("rememberme")
                // 有效时间200s
                .tokenValiditySeconds(200)
                //对Cookie信息进行持久化管理
                .tokenRepository(tokenRepository());
    }
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository(){
        JdbcTokenRepositoryImpl jr = new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        return jr;
    }
  • 测试———在登录时选中记住我后,每次登录数据表中token都不相同,在点击注销后,表会清空
    如果用户是在Token有效期后自动退出的,那么数据表中的持久化用户信息不会随之删除,当用户再次进行访问登录时,则是在表中新增一条持久化信息。

CSRF防护功能

  • CSRF(跨站请求伪造),也叫一键攻击或者会话控制,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
  • CSRF攻击是黑客借助受害者的Cookie骗取服务器的信任,但是黑客并不能获取Cookie,也看不到Cookie的具体内容,即黑客能够做的就是伪造正常用户给服务器发送请求,执行请求中的命令,在服务端直接改变数据的值,而非窃取服务器中的数据。
    因此,针对CSRF攻击要保护的对象是哪些可以直接产生数据变化的服务,而对于读取数据的服务,可以不进行CSRF保护。
  • 对于CSRF的防御:验证HTTP Referer字段/在请求地址中添加Token并验证/在HTTP头中自定义属性并验证
  • CSRF防护功能关闭
    • SpringBoot整合SpringSecurity默认开启了CSRF防御功能,并要求数据修改的请求都需要Security的安全认证后才可以正常访问
  • 创建数据修改界面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户修改</title>
</head>
<body>
<div align="center">
    <form method="post" action="@/updateUser">
      <!--  <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>-->
        用户名: <input type="text" name="username" /><br />&nbsp;&nbsp;码: <input type="password" name="password" /><br />
        <button type="submit">修改</button>
    </form>
</div>
</body>
</html>
  • 编写后台控制层方法
@Controller
public class CSRFController {
    // 向用户修改页跳转
    @GetMapping("/toUpdate")
    public String toUpdate(){
        return "csrf/csrfTest";
    }
    // 用户修改处理提交,未具体进行业务实现(数据库中数据未修改)
    @ResponseBody
    @PostMapping(value = "/updateUser")
    public String updateUser(@RequestParam String username, @RequestParam String password, HttpServletRequest request){
        System.out.println(username);
        System.out.println(password);
        String csrf_token = request.getParameter("_csrf");
        System.out.println(csrf_token);
        return "ok";
    }
}
  • 效果测试——修改后报错403,因为已经默认开始CSRF功能,所有涉及数据修改方式的请求都会被拦截
    • 可以关闭默认开启的CSRF防御功能/也可以配置Security需要的CSRF Token
    • 在configure中加入http.csrf().disable();关闭服务 ——强行关闭会面临被攻击的危险
  • 针对Form表单数据修改的CSRF Token配置
  • Security支持在Form表单中提供一个携带CSRF Token信息的隐藏域,与其他修改数据一起提交
  • 上面的测试页面已经注释:<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>,也可以使用Thymeleaf支持的th:action属性th:action="@{/updateUser}",该请求会默认携带CSRF Token信息,无须手动添加。

Security管理前端页面

  • 除了通过Spring Security对后台增加权限控制,还可以使用Security与Thymeleaf整合实现前端页面管理
  • 添加thymeleaf-extras-springsecurity5依赖启动器
 		<dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
  • 修改前端页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>影视直播厅</title>
</head>
<body>
<h1 align="center">欢迎进入电影网站首页</h1>
<!-- sec:authorize="isAnonymous()" 判断用户是否未登录,只有匿名用户才会显示请登录-->
<div sec:authorize="isAnonymous()">
	<h2 align="center">游客您好,如果想查看电影<a th:href="@{/userLogin}">请登录</a> </h2>
</div>
<!--sec:authorize="isAuthenticated()" 判断用户是否已经登录,只有认证用户才会显示登录用户信息和注销链接-->
<div sec:authorize="isAuthenticated()">
<!--	分别显示用户名name和权限authority-->
	<h2 align="center"><span sec:authentication="name" style="color:#007bff"></span>
	您好您的用户权限为<span sec:authentication="principal.authorities" style="color:darkkhaki"></span>,您有权看以下电影 </h2>
	<form th:action="@{/mylogout}" method="post">
		<input th:type="submit" th:value="注销">
	</form>
</div>
<hr>
	<div sec:authorize="hasRole('common')">
		<h3>普通电影</h3>
		<ul>
			<li><a th:href="@{/detail/common/1}">飞驰人生</a></li>
			<li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li>
		</ul>
	</div>

	<div sec:authorize="hasAuthority('ROLE_vip')">
		<h3>VIP专享</h3>
		<ul>
			<li><a th:href="@{/detail/vip/1}">速度与激情</a></li>
			<li><a th:href="@{/detail/vip/2}">猩球崛起</a></li>
		</ul>
	</div>
</body>
</html>
  • 测试结果——登录index.html会发现功能太强大了…
    至此完成的一个小demo还蛮有成就感
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我是一名语言模型AI,无法为您提供具体的代码实现。但是我可以提供一些思路和步骤。 1. 准备好环境 首先需要安装Java、IntelliJ IDEA、MySQL等软件,并创建好相应的数据库和表。需要添加相关的依赖,比如spring-boot-starter、mybatis-plus-boot-starter等等,以及Ajax和LayUI的相关库。 2. 创建实体类和Mapper接口 创建员工和日志的实体类,以及对应的Mapper接口,用于对数据库进行操作。可以使用Mybatis-Plus的代码生成器来自动生成基础的代码。 3. 编写业务逻辑层 在业务逻辑层中编写增删改查等操作方法,以及一些自定义的业务方法,用于处理员工和日志的具体业务需求。 4. 编写Controller层 在Controller层中处理请求和返回数据,并调用相应的业务逻辑方法。可以使用Ajax来实现异步提交数据和返回结果,使用LayUI框架来美化页面和处理交互逻辑。 5. 添加安全认证和日志记录 添加安全认证功能,以实现员工的登录、登出等操作,并记录相关的日志信息。可以使用Spring Security等框架来实现认证和授权,并使用AOP技术来记录日志信息。 6. 测试和部署 在本地进行测试,并将代码打包部署到服务器上。可以使用Maven来管理依赖和构建项目,使用Docker等容器技术来部署和管理应用程序。 以上就是实现员工管理和日志管理的大致步骤和思路。当然,具体实现还需要根据具体的业务需求和技术选型来进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值