Spring Boot(五):Spring Boot 集成 Spring Security (I)

今天我们将在上一篇博客《Spring Boot(四):Spring Boot 集成 Thymeleaf》的基础上来集成Spring Security.

1. 添加Maven依赖

在pom.xml引用spring security.

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

添加Maven依赖后,运行项目,访问https://localhost:8443/SpringBootBase/ 浏览器会弹出如下身份验证框:

如图1所示:


图1

这是因为Spring Security对我们的工程默认使用"basic"身份认证,只要引入了Spring Security, 那么整个web应用都是安全的,所有资源访问都要经过身份验证授权才可以访问。可用user 及 随机密码登录,随机密码可从web应用启动日志查询, 如:

Using default security password: 89c19869-277c-4eba-89c8-590e0405ae84

当然也可以在application.properties里自定义username和password.

security.user.name=admin
security.user.password=123456

或者直接关闭这个默认的basic认证

security.basic.enabled=false

详情请参考官方文档:https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/html/boot-features-security.html

当你客制化自己的授权体系后,这个默认的"basic"认证将会自动被替代。

2. 配置spring security

我们还是以上一篇博客的工程代码为基础来整合Spring Security.

  • index.html: 登录界面
<!DOCTYPE html>
<html lang="zh-cn" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8"/>
    
    <title>Login</title>
    
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{css/customer/login.css}"/>
</head>

<body>
    <div class="container">
        <h3 align="center">这是一个带有登录框的主页</h3> 
        <form class="form-signin" th:action="@{/login}" th:object="${user}" method="post">
            <h2 class="form-signin-heading">请 登 录</h2>
            <input type="text" class="form-control" placeholder="账号" th:field="*{username}"/>
            <input type="password" class="form-control" placeholder="密码" th:field="*{password}"/>
            <p th:if="${param.logout}" class="error-code">已成功注销</p>
            <p th:if="${param.error}" class="error-code">用户名或者密码错误</p>
            <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
        </form>
    </div>
</body>
</html>

这回我们新增了两行代码:

<p th:if="${param.logout}" class="error-code">已成功注销</p>
<p th:if="${param.error}" class="error-code">用户名或者密码错误</p>

其中th:if="${param.logout}" 为Thymeleaf模板引擎判断语法,表示如果http post/get 请求的参数中带有logout,则显示已成功注销。

  • 再增加一个登录成功的欢迎界面welcome.html,带有注销按钮。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8"/>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="注 销"/>
        </form>
    </body>
</html>

th:inline="text"表示文本内联,即取${#httpServletRequest.remoteUser}值作为文本显示。

  • 在LoginController.java里新增两个controller
@RequestMapping(value ="/welcome", method = RequestMethod.GET)
String welcome() {
  return "welcome";
}

@RequestMapping(value ="/login", method = RequestMethod.GET)
String login(Model model, UserVO user) {
  model.addAttribute("user", user);
  return "index";
}

一个是用来welcome跳转,一个是用来login页面跳转。

  • 定制安全策略

接下来写个类WebSecurityConfig来继承WebSecurityConfigurerAdapter,用于确保经过认证的用户才能访问我们设置的需要经过验证的url.

package tech.onroad.springbootbase.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("admin").password("123456").roles("USER");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/welcome")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/css/**");
    }
}
auth.inMemoryAuthentication()
            .withUser("admin").password("123456").roles("USER");

表示在内存中创建一个username为admin, password为123456,role为USER的用户。但大多数web应用肯定会有自己的用户管理系统,这个我们在接下来的博客会另起一篇博客介绍,这里先用内存用户。

注意:前台login表单传过来的账户名及密码的参数名必须为username和password,否则Spring Security无法正确获取用户名及密码与后台用户系统进行匹配,具体原因是Sping Security的默认定义用户系统的用户名和密码为username和password. 如果想详细了解一下为什么,可参考auth.inMemoryAuthentication().withUser("admin").password("123456").roles("USER")源码。

configure(HttpSecurity http)方法是用来定义安全策略,如哪些url路径需要经过授权才能访问,哪些不用。如上面的代码中,"/"就不需要授权就可以访问,即我们可以正常访问https://localhost:8443/SpringBootBase/,而不需要用户授权。

当一个用户成功登录后,即Spring Security认证成功后,我们的web应用将重定向到之前用户请求的页面,也可以客制化,使用defaultSuccessUrl方法将其重定向到指定页面。loginPage("/login")表示在没有授权前,任何访问需要授权才能访问的页面都会先跳转到/login登录页面。

web.ignoring().antMatchers("/css/**");表示所以css目录下的静态资源都不作拦截。

3. 验证

按照代码逻辑:我们访问https://localhost:8443/SpringBootBase/,首先会跳转到index.html界面(因为https://localhost:8443/SpringBootBase/https://localhost:8443/SpringBootBase/index.html是一样的),然后输入账户名admin及密码123456,点击登录,将用户名及密码传到后台进行匹配,如果成功,则跳转到welcome.html界面,如果失败,则会默认指向/login?error,而从LoginController, 又将其定向到index.html页面,提示用户名或者密码错误。点击welcome界面注销按钮,则会默认跳转到/login?logout, 所以又回到index.html页面,显示已成功注销。

我们来运行一下,看结果是不是一样的?

  1. 访问https://localhost:8443/SpringBootBase/, 如图2所示

    图2

  2. 输入admin及密码123456,点击登录,成功跳转到welcome界面,如图3所示


    图3
  3. 点击注销登出,如图4所示


    图4
  4. 输入错误的账户名或密码,则得图5结果


    图5

一切如我们预期执行,说明我们spring security集成成功了。当然,这只用了spring security最简单的功能,还有自定义用户系统等等,我们接下来会慢慢介绍。


完整代码可到我的github下载:
https://github.com/onroadtech/SpringbootBase/
branch: master
commit-id: 2872ee008f23197b5fa28acee95aae378d4d1f01


本博文已同步发表于我的个人博客网站,欢迎转载指正并注明出处。
个人博客: www.onroad.tech
指正邮箱: onroad_tech@163.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348