SpringSecurity 简单使用_springsecurity使用_風栖祈鸢的博客-程序员宝宝

技术标签: spring  java  安全  SpringBoot  

SpringSecurity 简单使用

在 Web 开发中安全是不可忽视的问题(软件安全技术!),现在从 SpringSecurity 和 Shiro 两个框架来学习一下安全框架在 Web 应用中的使用。

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。

Spring Security是一个框架,它关注于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正威力在于它可以多么容易地扩展以满足定制需求。

在 Web 开发中,安全是非常重要的一个方面。Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两种需求 Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

简单来说,SpringSecurity 框架可以进行用户认证和授权,简化了原本 Web 应用中过滤器、拦截器冗余的代码!

1. 环境搭建

新建项目 SpringBoot-07-Security,添加 Web 模块和 Thymeleaf 模块。

然后在项目中导入静态资源(css、js、html),在 static 和 templates 目录下,资源引用自

https://gitee.com/cao_jianhua/spring-security?_from=gitee_search

目录结构:(resource)

static

—qiyuan

——css

——js

templates

—index.html

—views

——level1

———1.html

———2.html

———3.html

——level2()

——level3()

——login.html

然后在配置文件中关闭 Thymeleaf 的缓存

spring.thymeleaf.cache=false

再写一个控制器尝试访问一下页面

@Controller
public class RouterController {
    
    // 使用多个请求,要用 { } 括起来表明是数组!
    @RequestMapping({
    "/","/index"})
    public String index(){
    
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
    
        return "views/login";
    }

    // 按照请求的等级和 id 进入对应的页面
    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id")int id){
    
        return "views/level1/"+id;
    }
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id")int id){
    
        return "views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id")int id){
    
        return "views/level3/"+id;
    }
}

通过请求能访问到页面,环境搭建就完成了!

2. 用户认证和授权

为了用 SpringSecurity 实现安全控制,需要引入 spring-boot-starter-security 依赖!

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

在使用前,先了解三个重要的类和注解

  • WebSecurityConfigrerAdapter:自定义 Security 策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启 WebSecurity 模式(@Enable 就是开启!)

SpringSecurity 的主要目标就是认证授权

2.1 用户授权

导入相关依赖后,需要进行 SpringSecurity 的配置,在 com.qiyuan.config 包下创建 SecurityConfig 类,继承 WebSecurityConfigrerAdapter

使用 Ctrl + O 可以查看父类方法并重写 configure(HttpSecurity http) 方法,进行授权的设置(重写的内容可以在源码的注释中查看!)

// AOP 拦截器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    // 授权
    protected void configure(HttpSecurity http) throws Exception {
    
        // 链式编程
        // 请求授权的设置
        http.authorizeRequests()
                .antMatchers("/","/index").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");
        // 没有权限前往登录页面
        http.formLogin();
    }
}

其中可以直接设定前往某页面所需的权限,再通过 formLogin() 方法使无权限的前往登录页面(这个登录页页面甚至是 SpringSecurity 自带的,请求为 /login)。

2.2 用户认证

再重写 configure(AuthenticationManagerBuilder auth) 方法进行认证的设置

// AOP 拦截器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    ...

    @Override
    // 认证
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        // auth.jdbcAuthentication() 从数据库获取认证信息
        // 现在没有数据库,只能从内存中获取认证信息
        auth.inMemoryAuthentication()
                .withUser("qiyuanc1").password("0723").roles("VIP1")
                .and()
                .withUser("qiyuanc2").password("0723").roles("VIP1","VIP2")
                .and()
                .withUser("qiyuanc3").password("0723").roles("VIP1","VIP2","VIP3");
    }
}

如果就这样运行,按照设置好的用户名密码登录时会报错:There is no PasswordEncoder mapped for the id "null",这是因为将用户名密码保存在内存中,需要对密码进行加密,防止反编译!SpringSecurity 推荐使用 BCrypt 加密方式,修改代码为

// AOP 拦截器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    ...

    @Override
    // 认证
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        // auth.jdbcAuthentication() 从数据库获取认证信息
        // 现在没有数据库,只能从内存中获取认证信息
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("qiyuanc1").password(new BCryptPasswordEncoder().encode("0723")).roles("VIP1")
                .and()
                .withUser("qiyuanc2").password(new BCryptPasswordEncoder().encode("0723")).roles("VIP1","VIP2")
                .and()
                .withUser("qiyuanc3").password(new BCryptPasswordEncoder().encode("0723")).roles("VIP1","VIP2","VIP3");
    }
}

这样内存中的身份验证就搞定了,但 JDBC 身份验证还没试过,具体参考

https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle/#jc-authentication-inmemory

后面综合到一起的时候再做。

3. 注销及权限控制

3.1 注销

和开启 SpringSecurity 自带的登录功能一样,一句话开启注销功能,对应的请求默认为 /logout

        // 没有权限前往登录页面
        http.formLogin();
        // 开启注销功能
        http.logout();

然后在前端添加发起注销请求的按钮

<a class="item" th:href="@{/logout}">
    <i class="sign-out icon"></i> 注销
</a>

这样就能注销当前登录的用户了,不过注销后默认会回到登录界面,再进行设置使注销后回到首页

        // 开启注销功能,注销成功回到首页
        http.logout().logoutSuccessUrl("/");

这样注销功能就非常完善了!

3.2 权限控制

现在又有了新的权限控制问题:对于不同的角色(如 VIP1 和 VIP2)和不同的状态(登录和未登录),他们看同一页面的内容应该是不同的。如,VIP1 应该无法看到 level2 的内容;登录用户不用看到登录按钮,而要看到注销按钮;未登录用户不用看到注销按钮,而要看到登录按钮。这就是真实网站的情况!

想要实现这种涉及前端显示的权限控制,当然需要前端页面的支持,结合 Thymeleaf 中的一些功能。

导入 Thymeleaf 扩展 SpringSecurity 的依赖

<!-- Thymeleaf 扩展 SpringSecurity -->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

还需要在前端页面导入命名空间

xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"

然后在前端页面中,使用 Thymeleaf 的 sec:authorize 标签及 isAuthenticated() 方法判断当前角色是否经过验证;

如未登录时,该标签取非判断生效,显示登录按钮

<!--如果没有登录-->
<div sec:authorize="!isAuthenticated()">
    <a class="item" th:href="@{/login}">
        <i class="address card icon"></i> 登录
    </a>
</div>

以及登录后,该标签本身判断生效,显示用户名、角色及注销按钮

<div sec:authorize="isAuthenticated()">
    <!--如果登录则显示 用户名 角色-->
    <a class="item">
        用户名:<span sec:authentication="name"></span>
        角色:<span sec:authentication="authorities"></span>
    </a>
</div>
<div sec:authorize="isAuthenticated()">
    <!--如果登录则显示 注销-->
    <a class="item" th:href="@{/logout}">
        <i class="sign-out icon"></i> 注销
    </a>
</div>

这样登录和未登录用户的界面就区分开了!

现在还剩下一个问题:不同权限的用户应该要看到不同的内容。同样使用 sec:authorize 标签和 hasAuthority() 方法实现

<div class="ui raised segment" sec:authorize="hasAuthority('ROLE_VIP1')">
    <!-- VIP1 的内容 -->
</div>
<div class="ui raised segment" sec:authorize="hasAuthority('ROLE_VIP2')">
    <!-- VIP2 的内容 -->
</div>
<div class="ui raised segment" sec:authorize="hasAuthority('ROLE_VIP3')">
    <!-- VIP3 的内容 -->
</div>

前端通过获取当前用户的权限信息,判断是否显示某些内容!如此权限控制也完成了!

4. 记住我及定制登录页

4.1 记住我

记住我是一个非常常用且实用的功能,避免了进入同一个网站需要多次登录的情况。在 SpringSecurity 框架的帮助下,想要开启记住我功能,只需在 configure(HttpSecurity http) 中开启

        // 开启记住我功能
        http.rememberMe();

这样在自带的登录页上就会出现记住我的选项了!

选择记住我并登录后,在浏览器的控制台中查看 Cookie,可以看到出现了一条名为 remember-me 的 Cookie,默认保留14天,这就是 SpringSecurity 帮我们创建的了

注销时,SpringSecurity 又会将这条 Cookie 设置为空

使用记住我后免登录的流程为:登录成功后,服务器将 Cookie 发送给浏览器于本地保存,以后发起请求时带上这条 Cookie,服务器检查通过则不用登陆;注销操作则会删除这个 Cookie。

4.2 定制登录页

之前使用的登录页面都是 SpringSecurity 自带的,虽然也挺好看的,但实际业务中还是要使用自己写的。

想要更换登录页面,只需添加 http.formLogin() 的设置,如

        http.formLogin().loginPage("/toLogin");

这样前往登录页面的请求就是 /toLogin 了,同时官方的登录页面已经被覆盖,前端的登录请求也需要由 /login 修改为 /toLogin

这就产生了一个问题:之前使用官方的登录页面,SpringSecurity 可以直接获取我们登录的参数,现在使用了自己的登录页面,要怎么把参数交给 SpringSecurity 呢?

解决方法同上,也是扩展 http.formLogin() 的设置即可

        http.formLogin()
                .loginPage("/toLogin")
                // 登录需要的参数
                .usernameParameter("username")
                .passwordParameter("password")
                // 登录请求交给 SpringSecurity
                .loginProcessingUrl("/login");

为了让 SpringSecurity 获得登录的参数,需要配置接收的参数,即 username 和 password;同时要设置将登录请求提交给 /login,即交给 SpringSecurity 处理。

之前添加的记住我功能也需要扩展配置,前端添加记住我的选择栏,提交参数为 remeber

<div class="field">
    <input type="checkbox" name="remember">记住我
</div>

然后添加 http.remeberMe() 的设置

        http.rememberMe().rememberMeParameter("remeber");

最后还会遇到一个问题:注销请求 /logout 找不到页面。这是由于 SpringSecurity 开启了 CSRF 防御功能导致的,logout 请求需要以 post 方式提交才可以。不过由于链接不是表单,不好调整,可以直接关闭此功能

        // 关闭 csrf 防御
        http.csrf().disable();

至此使用自己的登录页面,所有功能都是成功的了!其实就相当于套了层皮(自己的登录页),实际的登录参数仍要交给 SpringSecurity 去处理,只要配置好数据的转发就好了。

5. 总结

对 SpringSecurity 框架简单地使用后发现,它其实就是对过滤器和拦截器进行了封装,使得进行认证和授权变得更加简单,同时它还运用了 AOP 的方式,不影响之前的代码,只要横切进去,管理安全相关的事务就可以了。非常好用,下次还用!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43560701/article/details/122681166

智能推荐

如何使用VM虚拟机_盏茶作酒的博客-程序员宝宝

虚拟机就是在电脑上同时运行几个系统的工具,省去了买许多电脑的烦恼。可以用它来测试系统好不好用。下面,小编教大家如何使用虚拟机吧!{这样用win732位和VMware workstation14做教程}如果对你有帮助,请点击【有得】哦!如何使用VM虚拟机工具/原料Windows电脑一台(64位)首先,检查电脑配置是否符合VM虚拟机要求1右键点击桌面"计算机"(Win8为"这台电...

使用BottomSheetBehavior时遇到的坑(Support Library 25)_莫鹏飞不是MP4的博客-程序员宝宝

最近空闲的时候在研究Android Material Design,在看了严振杰大神的博客 Material之Behavior实现支付宝密码弹窗 仿淘宝/天猫商品属性选择 纸上得来终觉浅,于是决定自己试着用一用文中提到的BottomSheetBehavior。 具体用法这里不多说,感兴趣的朋友自己去看博客,大神写的很详细。 主要实现功能如下面两张图(点击按钮控制底部栏的出现和消失):

Git常用基础指令_git的am指令_amberoot_源狼乐的博客-程序员宝宝

git --version 查看git的版本信息 git config --global user.name 查看当前登录用户名 git config --global user.email 查看当前登录邮箱 git config --global user.name Git账号 设置Git账号 git config --gl...

“WCHAR *“ 类型的实参与 “LPSTR“ 类型的形参不兼容_FalsePlus的博客-程序员宝宝

在VS2013编译器中直接输入的字符串常量(如“abc”)默认是以const char *的格式(即ANSI编码)储存的,因此会导致类型不匹配的编译错误。方法是右击“解决方案资源管理器”中的项目,“属性→配置属性→常规→项目默认值→字符集”,默认的选项是“使用多字节字符集”,将它改为“使用Unicode字符集”即可。这样,输入的字符串会默认以const wchar_t *格式储存。...

soap学习笔记(六)----使用Axis2传输附件(AXIS2 MTOM) (上)_ZLHRoar的博客-程序员宝宝

本文介绍如何使用Axis2传递附件。 1.工作环境IDE: Eclipse 3.1.2jdk: jdk1.5.0_04Tomcat: apache-tomcat-5.0.28AXIS2:1.0(war版本和bin版本)2.实现   在Eclipse新建一个动态web工程,在WEB-INF\lib下加入axis2所需的jar包。本例的是一个系统的用户上传下...

Zigbee——cc2530串口通信基础_cc2530zigbee模块串口通信实验_狐冲君的博客-程序员宝宝

下删除无关文件项,直接下载到板子,打开串口调试助手即可看到hellow world

随便推点

35岁的程序员:第10章,排行榜_罗宾王的博客-程序员宝宝

  程强还是有一些商业头脑的,他发现很多女艺人下面都有一群粉丝,这些粉丝有些是富二代,有些是屌丝,他们对女艺人花不一样的钱享受不一样的服务,但相同的是他们都肯为女艺人花钱。程强为了让APP能快速赚到钱早就开通了支付功能,但是现在这项功能也只是用于商家给女艺人下订单和支付平台手续费,而一开始自己吹嘘的能够让很多商家入驻的事情,程强现在自己都没有太多信心。他冥思苦想,终于有一天晚上躺在床上,脑子里灵光一闪有了主意,第二天他叫来陈超,激动地陈述了自己的想法。  新版本上线后,郭云他们难得有一点空闲时间,正在感叹

输入三角形的三边,判断能否构成三角形,若可以则输出三角形的类型_输入三角形的三条边,判断能否构成三角形_a pessimist.的博客-程序员宝宝

//输入三角形的三边,判断能否构成三角形,若可以则输出三角形的类型//2019.12.25#include &lt;stdio.h&gt;int main(){int a,b,c;printf(“请输入三个边长\n”);scanf("%d %d %d",&amp;a,&amp;b,&amp;c);if(a+b&gt;c&amp;&amp;a+c&gt;b&amp;&amp;b+c&...

字符串的简单处理问题(包含断言用法)_徐安霖的博客-程序员宝宝

字符串的拷贝#include&lt;stdio.h&gt;#include&lt;assert.h&gt;#include&lt;string.h&gt;void mystrc1(char *des,char src){int i;for(i-0;src[i]=’\0’;i++)//(src+1){des[i]=src[i];}des[i]=’\0’;}//des=src...

IDC:中国人工智能及自动化市场十大预测_AI_Pro的博客-程序员宝宝

国际数据公司(IDC)于近日发布了《IDC FutureScape: 全球人工智能(AI)及自动化市场 2022 预测——中国启示》报告。在报告中,IDC 分析师团队描述了影响 IT 和业务决策者负责该项支出并有效利用相关解决方案的主要驱动因素,并给出了未来五年有关人工智能和自动化市场的十大预测。IDC 2022 年中国人工智能及自动化市场的十大预测具体内容如下:预测一到 2023 年,欧洲、美国和亚洲等地区会相继出现不同的人工智能监管法规,这些地区一方面将鼓励区域性的人工智能解

(二十七)Spring Boot使用@Async实现异步调用:使用Future以及定义超时[email protected]_ZerahMu的博客-程序员宝宝

本文主要实现Future的使用方法以及对异步执行的超时控制,关于异步任务Aynsc的使用参考前面几篇文章一、定义异步任务首先,我们先使用@Async注解来定义一个异步任务,这个方法返回Future类型,具体如下:@[email protected] class Task { public static Random random = new Random(); ...

推荐文章

热门文章

相关标签