当前位置:   article > 正文

SpringBoot 全家桶 | SpringSecurity实战_spring-boot-starter-security 实战

spring-boot-starter-security 实战

介绍

Spring Security 是一个功能强大且高度可定制的身份证认证和访问控制框架。其最重要的两部分是认证(Authentication)和授权(Authorization)。

本实例使用框架:

Spring Security 后端实例

引入包

我们引入spring-boot-starter-security包,同时为完成实例引入了其他的包,这里不多做介绍

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

</dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot-security?charset=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
Security配置文件

我们创建一个Security的配置类,并继承WebSecurityConfigurerAdapter类,加上@EnableWebSecurity注解,表示启用Security,下面看详细代码

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDao userDao;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
              .antMatchers("/css/**", "/", "/index", "/loginPage").permitAll() // 无需认证
                .anyRequest().authenticated() // 其他请求都需要认证
        ;

        http.formLogin() // 开启登录,如果没有权限,就会跳转到登录页
                .loginPage("/loginPage") // 自定义登录页,默认/login(get请求)
                .loginProcessingUrl("/login") // 登录处理地址,默认/login(post请求)
                .usernameParameter("inputEmail") // 自定义username属性名,默认username
                .passwordParameter("inputPassword") // 自定义password属性名,默认password
        ;

        http.rememberMe() // 开启记住我
                .rememberMeParameter("rememberMe") // 自定义rememberMe属性名
        ;

        http.logout() // 开启注销
                .logoutUrl("/logout") // 注销处理路径,默认/logout
                .logoutSuccessUrl("/") // 注销成功后跳转路径
        ;

        http.csrf().disable(); // 禁止csrf
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            xyz.zyl2020.security.entity.User user = userDao.findByUsernameOrEmail(username, username);
            if (user == null) {
                throw new UsernameNotFoundException("账号或密码错误!");
            }
            String[] roleCodeArray = user.getRoles().stream().map(Role::getCode).toArray(String[]::new);

            return User.withUsername(user.getUsername())
                    .password(user.getPassword())
                    .roles(roleCodeArray)
                    .build();
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 重写configure(HttpSecurity http)方法,用于配置http请求认证
    • http.authorizeRequests()为请求授权,可以定义哪些资源无需认证,哪些资源需要认证
    • http.formLogin() 开启form表单登录,可自定义登录页,登录请求处理,还有账号、密码使用的参数等
    • http.rememberMe() 开启记住我功能
    • http.logout()开户注销功能,并可自定义注解成功跳转路径
    • http.csrf()CSRF防护,默认是开启的。若开启,则在表单中增加 input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
  • userDetailsService() 配置鉴权,用户验证用户及权限。此时可以从数据库中查出用户及权限信息,构建UserDetailsService类并返回,进行下一步验证,由Spring Security自行验证
  • passwordEncoder() 配置密码编码格式,不启用会报下面异常,这里我们使用BCrypt。在用户注册时,密码使用相同的编码格式存库,BCrypt.hashpw("password", BCrypt.gensalt())
2020-08-24 22:03:20.014 ERROR 4692 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
	......

  • 1
  • 2
  • 3
  • 4
  • 5
Controller配置
@Controller
public class RouteController {

    @RequestMapping({"/", "/index"})
    public String index() {
        return "index";
    }

    @GetMapping("/loginPage")
    public String loginPage() {
        return "login";
    }

    @GetMapping("/currentUser")
    public String getUsername() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            return null;
        }
        UserDetails userDetails = (UserDetails) principal;
        return userDetails.getUsername();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • index()指向主页,对应resources/templates/index.html
  • loginPage()指向登录页,对应resources/templates/login.html

Service层、Dao层、和实体类查阅下面的【完整代码】吧,另外用户、角色、及其关系的初始化,在test代码中

Spring Security 前端使用thymeleaf

登录页 - 表单的使用
<form class="form-signin" action="/login" method="post">
    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
    <label for="inputEmail" class="sr-only">Email address</label>
    <input type="email" id="inputEmail" name="inputEmail" class="form-control" placeholder="Email address" required autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" id="inputPassword" name="inputPassword" class="form-control" placeholder="Password" required>
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" name="rememberMe"> Remember me
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    <p class="mt-5 mb-3 text-muted">&copy; 2017-2020</p>
</form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • action="/login"对应着后台配置loginProcessingUrl("/login"),这两个路径要一致
  • 用户名、密码、记住我的name名,与后台配置要保持一致

做好以前配置,提交表单后会将用户名、密码、记住我信息提交致后台进行认证授权

主页 - Thymeleaf与Security的集成

thymeleaf与Security集成,主要使用引入的thymeleaf-extras-springsecurity5

<!doctype html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    // 代码省略...
</head>
<body>
<div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
    <h5 class="my-0 mr-md-auto font-weight-normal">Welcome!</h5>
    <nav class="my-2 my-md-0 mr-md-3">
        <div sec:authorize="!isAuthenticated()">
            <a class="btn btn-outline-primary" href="/loginPage">Login</a>
        </div>
        <div sec:authorize="isAuthenticated()">
            <span sec:authentication="name"></span>
            <a class="btn btn-outline-primary" href="/logout">Logout</a>
        </div>
    </nav>

</div>

<div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
    <h1 class="display-4">Pricing</h1>
    <p class="lead">Quickly build an effective pricing table for your potential customers with this Bootstrap example.
        It’s built with default Bootstrap components and utilities with little customization.</p>
</div>

<div class="container">
    <div class="card-deck mb-3 text-center">
        <div class="card mb-4 shadow-sm" sec:authorize="hasAnyRole('FREE')">
            <div class="card-header">
                <h4 class="my-0 font-weight-normal">Free</h4>
            </div>
            <div class="card-body">
                <h1 class="card-title pricing-card-title">$0 <small class="text-muted">/ mo</small></h1>
                <ul class="list-unstyled mt-3 mb-4">
                    <li>10 users included</li>
                    <li>2 GB of storage</li>
                    <li>Email support</li>
                    <li>Help center access</li>
                </ul>
                <a class="btn btn-lg btn-block btn-primary" href="#">Get started</a>
            </div>
        </div>
        <div class="card mb-4 shadow-sm" sec:authorize="hasAnyRole('PRO')">
            <div class="card-header">
                <h4 class="my-0 font-weight-normal">Pro</h4>
            </div>
            <div class="card-body">
                <h1 class="card-title pricing-card-title">$15 <small class="text-muted">/ mo</small></h1>
                <ul class="list-unstyled mt-3 mb-4">
                    <li>20 users included</li>
                    <li>10 GB of storage</li>
                    <li>Priority email support</li>
                    <li>Help center access</li>
                </ul>
                <a class="btn btn-lg btn-block btn-primary">Get started</a>
            </div>
        </div>
        <div class="card mb-4 shadow-sm" sec:authorize="hasAnyRole('ENT')">
            <div class="card-header">
                <h4 class="my-0 font-weight-normal">Enterprise</h4>
            </div>
            <div class="card-body">
                <h1 class="card-title pricing-card-title">$29 <small class="text-muted">/ mo</small></h1>
                <ul class="list-unstyled mt-3 mb-4">
                    <li>30 users included</li>
                    <li>15 GB of storage</li>
                    <li>Phone and email support</li>
                    <li>Help center access</li>
                </ul>
                <a class="btn btn-lg btn-block btn-primary">Get started</a>
            </div>
        </div>
    </div>

</div>
</body>
</html>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 引入xmlns:sec="http://www.thymeleaf.org/extras/spring-security"命名空间,便可在html中使用security功能了
  • sec:authorize="isAuthenticated()"标签表示已验证通过,即登录成功
  • sec:authentication="name" 获取当前用户名
  • sec:authorize="hasAnyRole('FREE')"表示拥有指定角色方可显示内容
  • sec:authorize="hasAnyAuthority('user:add')"表示拥有指定权限方可显示内容

更多功能请参照官网

参考

  1. Spring Security
  2. thymeleaf-extras-springsecurity
  3. Thymeleaf + Spring Security integration basics
  4. Bootstrap4

完整代码

springboot-security

登录前.png
登录页.png
登录后.png
福利.jpg

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号