(1). 概述

在这里先对Spring Security进行一个简单的入门,入门案例的要求是,判断用户是否具备有URL访问的权限.

(2). 项目目录结构如下

lixin@lixin GitWorkspace % tree spring-security-example 
spring-security-example
├── README.md
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── help
│   │   │       └── lixin
│   │   │           └── security
│   │   │               └── example
│   │   │                   ├── Application.java
│   │   │                   ├── access
│   │   │                   │   └── PermissionVaidator.java
│   │   │                   ├── config
│   │   │                   │   └── WebSecurityConfig.java
│   │   │                   ├── controller
│   │   │                   │   └── HelloController.java
│   │   │                   └── userdetails
│   │   │                       └── DefaultUserDetailsService.java
│   │   └── resources
│   │       └── static
│   │           └── login.html
│   └── test
└── target

(3). WebSecurityConfig

package help.lixin.security.example.config;

import help.lixin.security.example.userdetails.DefaultUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();


        // 注意:当你发现调用不了UserDetailsService的时候,要注意关闭csrf
        http.formLogin()
                // 登录成功后,必须要是POST请求
                // .successForwardUrl("/main.html")
                // 登录页面
                .loginPage("/login.html")
                // 登录处理URL(Filter[UsernamePasswordAuthenticationFilter])
                .loginProcessingUrl("/login");


        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = http.authorizeRequests();
        expressionInterceptUrlRegistry
                // 允许/login.html不需要认证.
                .antMatchers("/login.html").permitAll()
                // .antMatchers("/hello").hasRole("ADMIN")
                // 所有请求都要经过access验证,才能访问
                .anyRequest().access("@permissionVaidator.hasPermission(request,authentication)");
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetailsService userDetailsService = new DefaultUserDetailsService(passwordEncoder);
        return userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

(4). DefaultUserDetailsService

package help.lixin.security.example.userdetails;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;

// 加载用户信息.
public class DefaultUserDetailsService implements UserDetailsService {

    private PasswordEncoder passwordEncoder;

    public DefaultUserDetailsService(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // TOOD lixin 去Repository中Query,如果,查找不到,则抛出异常:UsernameNotFoundException

        UserDetails user = User.withUsername("lixin")
                .password(passwordEncoder.encode("123456"))
                // 用户具有哪些资源(URI)的权限
                .authorities("/hello", "/test")
                .build();
        return user;
    }
}

(5). PermissionVaidator

package help.lixin.security.example.access;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * 自定义权限验证
 */
@Component
public class PermissionVaidator {

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        // 请求的URL
        String requestURI = request.getRequestURI();

        // 用户登录后的权限
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority grantedAuthority : authorities) {
            // 建议这里用Ant表达式对URL进行判断,准确性会更好一些.
            if (grantedAuthority.getAuthority().equals(requestURI)) {
                return true;
            }
        }
        return false;
    }
}

(6). HelloController

package help.lixin.security.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

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

(7). Application

package help.lixin.security.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

(8). login.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>user login</title>
</head>
<body>
    <form action="/login" method="post">
        用户名:<input type="text" name="username">    <br/>
        密码:<input type="password" name="password">  <br/>
        <input type="submit" value="登录">
    </form>
</body>
</html>

(9). pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>help.lixin.security.example</groupId>
    <artifactId>spring-security-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.9.RELEASE</version>
        <relativePath />
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
	
</project>

(10). 总结

通过Spring Security的简单配置,就可以实现对URL资源的认证和鉴权操作.