Spring Boot2 实战系列之使用 Spring Security 实现登录

前言

在之前的一篇博文 Spring Boot2 实战系列之语言国际化 中利用了 BootStrap 官网的一个登录页面实现了语言国际化,这次在此之上进行改进,增加 Spring Security 的功能,使其能对登录用户进行认证。

Spring Boot 已经为 spring security 进行了自动配置,只需要引入以下依赖即可:

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

在这次这个例子中主要演示了用户登录,用户不同权限的展示和退出登录的功能。

创建项目

项目结构图如下:
Spring Boot2 实战系列之使用 Spring Security 实现登录_第1张图片

pom 依赖文件如下:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.2.5.RELEASEversion>
		<relativePath/> 
	parent>
	<groupId>top.yekonglegroupId>
	<artifactId>springboot-security-login-sampleartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>springboot-security-login-samplename>
	<description>Security login sample for Spring Bootdescription>

	<properties>
		<java.version>1.8java.version>
	properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-securityartifactId>
		dependency>
		<dependency>
			<groupId>org.thymeleaf.extrasgroupId>
			<artifactId>thymeleaf-extras-springsecurity5artifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<scope>runtimescope>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintagegroupId>
					<artifactId>junit-vintage-engineartifactId>
				exclusion>
			exclusions>
		dependency>
		<dependency>
			<groupId>org.springframework.securitygroupId>
			<artifactId>spring-security-testartifactId>
			<scope>testscope>
		dependency>
	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

代码编写

application.properties

# 国际化i18n配置,(包名.基础名)
spring.messages.basename=i18n.login
spring.messages.encoding=UTF-8

# Thymeleaf 配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# 禁止缓存
spring.thymeleaf.cache=false

Web 安全配置,使用了基于内存的用户认证,初始化了3个用户,有 USER 和 ADMIN 两种权限,不用在前面加 “ROLE_”, spring boot 会自动加上
WebSecurityConfig.java

package top.yekongle.security.login.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/** 
* @Description: 
* @Author: Yekongle 
* @Date: 2020年5月17日
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("Jack").password("$2a$10$jxi3LTkdNRPMv0Cb6ag9kO/7xYUnkN.9aTKoSag8u3Ep8pBe2rMMm").roles("USER")
			.and()
			.withUser("David").password("$2a$10$jxi3LTkdNRPMv0Cb6ag9kO/7xYUnkN.9aTKoSag8u3Ep8pBe2rMMm").roles("USER")
			.and()
			.withUser("Kevin").password("$2a$10$jxi3LTkdNRPMv0Cb6ag9kO/7xYUnkN.9aTKoSag8u3Ep8pBe2rMMm").roles("ADMIN");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.csrf().disable()
			.authorizeRequests()
			.antMatchers("/css/**", "/js/**", "/fonts/**", "/img/**").permitAll()
			.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
			.antMatchers("/admin/**").hasRole("ADMIN")
			.antMatchers("/login*", "/logout*").permitAll()
			.anyRequest().authenticated()
			.and()
			.formLogin()
			.loginPage("/login")
			.defaultSuccessUrl("/home.html")
			.failureUrl("/login?error=true").permitAll()
			.and()
			.logout()
			.deleteCookies("JSESSIONID")
			.logoutSuccessUrl("/logout.html?logSucc=true").permitAll();
	}
	
	/*
	 * public static void main(String[] args) { System.out.println(new
	 * BCryptPasswordEncoder().encode("A123456!")); }
	 */
	
}

WebMvcConfig.java

package top.yekongle.security.login.config;

import java.util.Locale;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

/** 
* @Description: I18n config
* @Author: Yekongle 
* @Date: Mar 22, 2020
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //设置对"/"的请求映射到login
        //如果没有逻辑业务,没有必要用控制器方法对请求进行映射
    	registry.addViewController("/").setViewName("forward:/login");
        registry.addViewController("/admin").setViewName("dashboard");
        registry.addViewController("/logout.html");
        registry.addViewController("/home.html");
    }
    
    /*
     * 配置拦截器获取URL中的 lang 参数 (?lang=zh_CN)
     * */
    @Override
    public void addInterceptors(final InterceptorRegistry registry) {
        final LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        registry.addInterceptor(localeChangeInterceptor);
    }
 
    /*
     * 注册我们自定义的区域解析器,
     * 一旦将区域解析器注册到Spring容器中
     * 则SpingBoot默认提供的区域解析器将不会自动注册
     */
    @Bean
    public LocaleResolver localeResolver() {
        final CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
        cookieLocaleResolver.setDefaultLocale(Locale.CHINESE);
        return cookieLocaleResolver;
    }
 
}

登录控制跳转
LoginController.java

package top.yekongle.security.login.controller;

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/** 
* @Description: 
* @Author: Yekongle 
* @Date: 2020年5月17日
*/
@Controller
public class LoginController {
	@GetMapping("/login")
	public String login() {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (auth instanceof AnonymousAuthenticationToken) {
			return "login";
		} else {
			return "home";
		}
	}
}

登录页面
login.html


<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstraptitle>
		
		<link href="/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}"  rel="stylesheet">
		
		<link href="/css/signin.css" th:href="@{/css/signin.css}"  rel="stylesheet">
	head>
 
	<body class="text-center">
	   
		<form class="form-signin" action="login" method='POST'>
		    <div th:if="${param.error != null}" class="alert alert-danger" >[[#{login.error}]]div>
			<img class="mb-4" src="/img/bootstrap-solid.svg" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign inh1>
			<label class="sr-only">Usernamelabel>
			<input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only">Passwordlabel>
			<input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" value="remember">  [[#{login.remember}]]
        label>
			div>
			<button class="btn btn-lg btn-primary btn-block" th:text="#{login.btn}" type="submit">Sign inbutton>
			<p class="mt-5 mb-3 text-muted">© 2017-2020p>
			<a class="btn btn-sm" th:href="@{/login(lang='zh_CN')}">中文a>
			<a class="btn btn-sm" th:href="@{/login(lang='en_US')}">Englisha>
		form>
		
		
	    <script th:src="@{/js/jquery-3.5.1.min.js}" type="text/javascript">script>
	    <script th:src="@{/js/bootstrap.min.js}" type="text/javascript">script>
	body>
html>

主页,注意下面这一句,只有拥有 “ADMIN” 角色的用户才能看到

<a sec:authorize="hasAuthority('ROLE_ADMIN')"  class="btn btn-outline-success my-2 my-sm-0" th:href="@{/admin}">Consolea>

home.html



<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Jekyll v3.8.6">
    <title>Carousel Template · Bootstraptitle>

    <link rel="canonical" href="https://v4.bootcss.com/docs/examples/carousel/">

    
  <link href="/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}"  rel="stylesheet">

<meta name="theme-color" content="#563d7c">


    <style>
      .bd-placeholder-img {
        font-size: 1.125rem;
        text-anchor: middle;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }

      @media (min-width: 768px) {
        .bd-placeholder-img-lg {
          font-size: 3.5rem;
        }
      }
    style>
    
    <link th:href="@{/css/carousel.css}" rel="stylesheet">
  head>
  <body>
    <header>
  <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
    <a class="navbar-brand" href="https://v4.bootcss.com/docs/examples/carousel/#">Carousela>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon">span>
    button>
    <div class="collapse navbar-collapse" id="navbarCollapse">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="https://v4.bootcss.com/docs/examples/carousel/#">Home <span class="sr-only">(current)span>a>
        li>
        <li class="nav-item">
          <a class="nav-link" href="https://v4.bootcss.com/docs/examples/carousel/#">Linka>
        li>
        <li class="nav-item">
          <a class="nav-link disabled" href="https://v4.bootcss.com/docs/examples/carousel/#" tabindex="-1" aria-disabled="true">Disableda>
        li>
      ul>
      <form class="form-inline mt-2 mt-md-0">
        <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
		
        <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Searchbutton> 
        <a sec:authorize="hasAuthority('ROLE_ADMIN')"  class="btn btn-outline-success my-2 my-sm-0" th:href="@{/admin}">Consolea> 
        <a class="btn btn-outline-success my-2 my-sm-0" th:href="@{/logout}">Sign outa>
      form>
    div>
  nav>
header>

<main role="main">

  <div id="myCarousel" class="carousel slide" data-ride="carousel">
    <ol class="carousel-indicators">
      <li data-target="#myCarousel" data-slide-to="0" class="">li>
      <li data-target="#myCarousel" data-slide-to="1" class="active">li>
      <li data-target="#myCarousel" data-slide-to="2" class="">li>
    ol>
    <div class="carousel-inner">
      <div class="carousel-item">
        <svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img"><rect width="100%" height="100%" fill="#777">rect>svg>
        <div class="container">
          <div class="carousel-caption text-left">
            <h1>Example headline.h1>
            <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.p>
            <p><a class="btn btn-lg btn-primary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">Sign up todaya>p>
          div>
        div>
      div>
      <div class="carousel-item active">
        <svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img"><rect width="100%" height="100%" fill="#777">rect>svg>
        <div class="container">
          <div class="carousel-caption">
            <h1>Another example headline.h1>
            <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.p>
            <p><a class="btn btn-lg btn-primary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">Learn morea>p>
          div>
        div>
      div>
      <div class="carousel-item">
        <svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img"><rect width="100%" height="100%" fill="#777">rect>svg>
        <div class="container">
          <div class="carousel-caption text-right">
            <h1>One more for good measure.h1>
            <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.p>
            <p><a class="btn btn-lg btn-primary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">Browse gallerya>p>
          div>
        div>
      div>
    div>
    <a class="carousel-control-prev" href="https://v4.bootcss.com/docs/examples/carousel/#myCarousel" role="button" data-slide="prev">
      <span class="carousel-control-prev-icon" aria-hidden="true">span>
      <span class="sr-only">Previousspan>
    a>
    <a class="carousel-control-next" href="https://v4.bootcss.com/docs/examples/carousel/#myCarousel" role="button" data-slide="next">
      <span class="carousel-control-next-icon" aria-hidden="true">span>
      <span class="sr-only">Nextspan>
    a>
  div>


  
  

  <div class="container marketing">

    
    <div class="row">
      <div class="col-lg-4">
        <svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 140x140"><title>Placeholdertitle><rect width="100%" height="100%" fill="#777">rect><text x="50%" y="50%" fill="#777" dy=".3em">140x140text>svg>
        <h2>Headingh2>
        <p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.p>
        <p><a class="btn btn-secondary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">View details »a>p>
      div>
      <div class="col-lg-4">
        <svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 140x140"><title>Placeholdertitle><rect width="100%" height="100%" fill="#777">rect><text x="50%" y="50%" fill="#777" dy=".3em">140x140text>svg>
        <h2>Headingh2>
        <p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.p>
        <p><a class="btn btn-secondary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">View details »a>p>
      div>
      <div class="col-lg-4">
        <svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 140x140"><title>Placeholdertitle><rect width="100%" height="100%" fill="#777">rect><text x="50%" y="50%" fill="#777" dy=".3em">140x140text>svg>
        <h2>Headingh2>
        <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.p>
        <p><a class="btn btn-secondary" href="https://v4.bootcss.com/docs/examples/carousel/#" role="button">View details »a>p>
      div>
    div>


    

    <hr class="featurette-divider">

    <div class="row featurette">
      <div class="col-md-7">
        <h2 class="featurette-heading">First featurette heading. <span class="text-muted">It’ll blow your mind.span>h2>
        <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.p>
      div>
      <div class="col-md-5">
        <svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 500x500"><title>Placeholdertitle><rect width="100%" height="100%" fill="#eee">rect><text x="50%" y="50%" fill="#aaa" dy=".3em">500x500text>svg>
      div>
    div>

    <hr class="featurette-divider">

    <div class="row featurette">
      <div class="col-md-7 order-md-2">
        <h2 class="featurette-heading">Oh yeah, it’s that good. <span class="text-muted">See for yourself.span>h2>
        <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.p>
      div>
      <div class="col-md-5 order-md-1">
        <svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 500x500"><title>Placeholdertitle><rect width="100%" height="100%" fill="#eee">rect><text x="50%" y="50%" fill="#aaa" dy=".3em">500x500text>svg>
      div>
    div>

    <hr class="featurette-divider">

    <div class="row featurette">
      <div class="col-md-7">
        <h2 class="featurette-heading">And lastly, this one. <span class="text-muted">Checkmate.span>h2>
        <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.p>
      div>
      <div class="col-md-5">
        <svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 500x500"><title>Placeholdertitle><rect width="100%" height="100%" fill="#eee">rect><text x="50%" y="50%" fill="#aaa" dy=".3em">500x500text>svg>
      div>
    div>

    <hr class="featurette-divider">

    

  div>


  
  <footer class="container">
    <p class="float-right"><a href="https://v4.bootcss.com/docs/examples/carousel/#">Back to topa>p>
    <p>© 2017-2020 Company, Inc. · <a href="https://v4.bootcss.com/docs/examples/carousel/#">Privacya> · <a href="https://v4.bootcss.com/docs/examples/carousel/#">Termsa>p>
  footer>
main>
    <script src="./Dashboard_Bootstrap_files/jquery.slim.min.js" th:src="@{/js/jquery.slim.min.js}" >script>
      <script>window.jQuery || document.write('
                    
                    

你可能感兴趣的:(Spring,Boot)