Spring Security小教程 Vol 1. 最简单的应用

噗噗虎和阿基拉的废柴大叔小课堂
本文阅读约须要10分钟 本期的配套视频讲解请访问https://www.bilibili.com/video/av45427128/

前言

本系列开坑编写的动机是为了在中文圈为Spring Security的推广、使用舔砖加瓦。由于Spring Security几乎对全部Spring应用都有存在的意义,因此咱们第一期就把Spring Security做为咱们教程的第一个话题。同期咱们也制做了每期10分钟的视频讲解,但愿能够弥补仅仅使用文字和图片描述一个技术知识点上给读者带来的困扰。css

最后,在本系列编写期间,咱们使用的SpringBoot版本为1.5;Security版本为5。如因版本差别致使的没法正常运行和获取指望结果的状况发生,能够经过评论私信给予咱们反馈。谢谢。html

第一期 快速为Spring Boot应用添加Spring Security

本期的任务清单

  1. 快速初始化一个Spring Boot项目
  2. 如何添加基于内存的用户鉴权功能
  3. 如何添加基于角色的访问控制逻辑

一、快速初始化一个Spring Boot项目

咱们推荐初学者若是对Maven的pom.xml编写和spring各类starter依赖不熟悉的状况下,使用Spring官方提供的项目初始化工具进行生成。start.spring.io/ 咱们的目的是初始化一个基于SpringBoot的Web应用,而且包含了Security的相关组件。因此在工具的Web界面上依赖组件部分,须要依次键入并选择Web、Security和Thymeleaf三个组件依赖。 点击生成后,浏览器便会自动下载一个项目工程压缩包。 前端

https://start.spring.io/

解压缩代码以后,经过IDEA导入后便会获得一个已经包含了Security的基础SpringBoot应用。 运行DemoApplication.java,在控制台看到应用启动完毕后,经过浏览器访问本机的127.0.0.1:8080端口,如看到提示输入用户名和密码的登陆对话框,那么就表明了Security已经成功的加载到了应用中。 java

默认登陆对话框

二、添加基于内存的用户鉴权功能

鉴权和访问控制的区分

鉴权和受权
首先,咱们要对Authentication和Authorization两个词作一个区别。在中文语义上咱们会把Authentication称为鉴权,用户认证,一般是一个对根据某些信息、好比用户名、密码、令牌等来辨别用户的过程。而Authorization在中文语义上能够理解为受权、访问控制。为了未来好区别咱们未来会统一把Authorization称为访问控制(Access Control),由于Authorization是根据用户的角色、权限等信息来判断目标行为、资源是否是能够被对应的用户使用、消费。 简单的说,鉴权就是用户登陆、受权就是权限控制。

而咱们的第二个任务就是配置Security框架使其能够正确的获取用户信息用于登陆检查。 咱们选择经过JavaConfig的方式类编写这个配置,类名咱们取名叫WebSecurityConfig,并键入下面的代码。spring

@EnableWebSecurity //配置注解
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
    //注入新的UserDetailsServiceBean
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        return manager;
    }
}
复制代码

咱们在代码中主要完成的工做就注入了一个\color{red}{UserDetailsService}的组件。那么什么是\color{red}{UserDetailsService}呢?数据库

UserDetailsService 和 UserDetails

UserDetails是咱们接触的第一个重要概念。在Spring Security的观点中,UserDetails就比如咱们自行设计系统的用户、帐户的概念。他包含了用户名、密码和其对应的授予权限。浏览器

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}
复制代码

UserDetailsSevice就是当前系统中如何获取库存用户信息的服务。bash

public interface UserDetailsService {
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}}
复制代码

在这里就能够简单的认为,在咱们输入用户名和密码以后,框架便会经过UserDetailsService 的实现类去寻找验证用户前端输入的用户名和密码是否正确,若是正确则返回UserDetails完成登陆操做。Security模式提供了许多种方式的用户信息管理服务实现,好比基于数据库、基于LDAP的。咱们当前使用的是最简单基于内存的用户管理实现InMemoryUserDetailsManager。app

InMemoryUserDetailsManager是Security提供的UserDetailsService的实现类
咱们经过新建InMemoryUserDetailsManager,而后经过createUser方法向其添加了一条用户记录。最终将其注入Spring框架,使其为咱们的应用在登陆时候能够正确的查找到咱们指望的用户记录。

@Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        return manager;
    }
复制代码

若是使用的是Sping Security5,则在password部分个字符串为.password("{noop}password")。不然可能会抛出一个异常。框架

重启应用后,从新访问http://127.0.0.1:8080/的测试应用,在输入用户名和密码后,则会提示目标访问的页面不存在的404错误。这起码证实登陆逻辑已经完成了,稍后简单编写一个简单的控制器和页面模板就能够完成第二个任务。 控制器代码MainController

@Controller
public class MainController {
    @RequestMapping("/")
    public String root() {
        return "index";
    }
复制代码

页面模板代码 index.html

<!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>
    <title>Hello Spring</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
    <h1>Hello Spring</h1>
</body>
</html>
复制代码

在登录后即可以看到咱们指望的hello页面。

index.html的访问页面

三、 如何添加基于角色的访问控制逻辑

刚刚一个任务咱们完成了Spring Security两个主要关注点之一的鉴权功能,如今咱们就开始实现一个最简单的访问控制逻辑。 首先,咱们先对当前任务的目标作一个简单的设计: 咱们将在MainController编写两个路径页面,分别是不须要访问控制的 ""路径和须要登陆控制的"\user"路径。

基于url的访问控制设计图

编写控制器

首先咱们根据设计将MainContoller的代码补全,添加新的url和对应的视图模板。user.html对先直接复制index.html,只是把内容改为Welcome User就能够了。 MainController .java

@Controller
public class MainController {
    @RequestMapping("/")
    public String root() {
        return "index";
    }
    @RequestMapping("/user")
    public String userIndex() {
        return "user";
    }
}
复制代码

user.html

<!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>
    <title>Welcome User</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
    <h1>Welcome User</h1>
</body>
</html>
复制代码

编写访问控制配置

咱们的配置类WebSecurityConfig是继承框架提供的\color{red}{WebSecurityConfigurerAdapter},其中有过一个重要的配置方案咱们关注下框架提供的默认值实现:

protected void configure(HttpSecurity http) throws Exception {
		logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
		http
			.authorizeRequests()
				.anyRequest().authenticated()
				.and()
			.formLogin().and()
			.httpBasic();
	}
复制代码

这一段代码主要搞事咱们一个HttpSecurity的配置主要有三点:

  1. authorizeRequests()下管理路径访问控制;
  2. formLogin()管理登陆表单配置;
  3. httpBasic()是否基于Http的验证配置。 后两点不是咱们的重点,咱们的目标是配置路径的访问控制,因此咱们须要在咱们本身的配置类覆写这个方法:
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // inde.html对应的url容许所任人访问
                .antMatchers("/").permitAll()
                // user.html对应的url,则须要用户有USER的角色才能够访问
                .antMatchers("/user").hasRole("USER")
                .and()
                .formLogin();
    }
复制代码

咱们使用了hasRole()基于角色的验证条件,让我再回顾下,以前咱们在用户鉴权部分,添加的用户记录的代码是怎么样的?咱们添加的user用户其也包含了一个USER的角色,而在访问控制的时候便会检查这一角色受权信息是否匹配。 若是使用的是Spring Security 4 如下的版本则可以使用

manager.createUser(User.withUsername("user").password("password").roles("USER").build());
复制代码

如使用的是5以上的版本,由于Spring Security中经过配置PasswordEncoder才能进行正确的密码加密操做User.withDefaultPasswordEncoder(),不然可能框架会报错。

manager.createUser(
    User.withDefaultPasswordEncoder()
        .username("user")
        .password("password")
        .roles("USER")
        .build());
复制代码

User.withDefaultPasswordEncoder() 是一个仅仅只能使用在示例应用的方法,由于Spring Security 5中对组件的生成机制进行了调整,使用以前的代码生成用户信息会致使没法判断使用哪一个PasswordEncoder。具体的细节在以后单独开一个专题进行说明。 感谢朝歌问弦反馈的问题。

重启应用,首先输入/路径,应用没有提示咱们任何登陆对话框,咱们就看到了Hello Spring的标题。

\路径

接着咱们再键入\user路径,由于访问控制检查到咱们没有完成登陆操做,则将咱们重定向到login页面完成登陆作操 最后,当咱们完成用户名和输入以后,由于用户角色与指望配置的一致,咱们得以访问目标/user页面。

\user页面
这样咱们也完成最简单的访问控制配置的任务。

结尾

咱们在本期中对SpringSecurity最重要的两个功能:用户鉴权和访问控制作了最简单的实现和配置。 在下一期,咱们将对用户鉴权部分的具体流程展开讲解。让咱们下期再见。

相关文章
相关标签/搜索