SpringBoot.3 使用SpringSecurity授权登陆

在Spring工程中经常会把一些常用的功能打包成一个模块提供给大家使用,SpringSecurity便是针对Web安全打包的一个模块,提供了一些基本比如身份验证、授权、Web安全等等代码。

下面会演示一个用户登陆的例子,应用设计三个页面 – home.htmlhello.htmllogin.html,其中hello.html需要登陆之后才能访问,如果用户点击了 home.html中的链接会先重定向到 login.html

参考:Securing a Web Application

构建不安全的页面

在演示Spring提供的安全模块之前,需要先构建一个不安全的网站,这个网站提供的任何页面都可以随意的访问。

  1. 工程结构,三部分:Gradle构建文件,Java代码,HTML资源文件
    ➜  spring-securing-web  tree .
    .
    ├── build.gradle
    └── src
        └── main
            ├── java
            │   └── hello
            │       ├── App.java
            │       └── MvcConfig.java
            └── resources
                └── templates
                    ├── hello.html
                    └── home.html
  1. 构建文件,和之前系列一致,仅仅在依赖地方有变化
    dependencies {
        compile("org.springframework.boot:spring-boot-starter-thymeleaf")
        testCompile("junit:junit")
    }
  1. Java代码非常简单,App.java 提供App运行入口,MvcConfig.java 用来注册URL和页面的映射关系。
    // App.java
    @SpringBootApplication
    public class App {

        public static void main(String[] args) {
            SpringApplication.run(App.class);
        }
    }
    // MvcConfig.java
    @Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {

        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/home").setViewName("home");
            registry.addViewController("/").setViewName("home");
            registry.addViewController("/hello").setViewName("hello");
            registry.addViewController("/login").setViewName("login");
        }
    }
  1. HTML资源文件为两个静态页面
    // home.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-springsecurity3">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
    <h1>Welcome!</h1>
    <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
    </html>

    // hello.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-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
    <h1>Hello world!</h1>
    </body>
    </html>
  1. 此时运行App.java中的main()方法,可以访问 http://localhost:8080/home,并从 home 页的连接跳转到 hello.html

添加安全的授权登陆

接下来会利用SpringSecurity模块提供的安全访问功能,把未授权用户的访问重定向到 login.html

  1. 添加依赖: spring-boot-starter-security
dependencies {
    ...
    compile("org.springframework.boot:spring-boot-starter-security")
    ...
}
  1. 添加授权登陆配置 – WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}
  1. 添加HTML页面 – login.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-springsecurity3">
        <head>
            <title>Spring Security Example </title>
        </head>
        <body>
            <div th:if="${param.error}">
                Invalid username and password.
            </div>
            <div th:if="${param.logout}">
                You have been logged out.
            </div>
            <form th:action="@{/login}" method="post">
                <div><label> User Name : <input type="text" name="username"/> </label></div>
                <div><label> Password: <input type="password" name="password"/> </label></div>
                <div><input type="submit" value="Sign In"/></div>
            </form>
        </body>
    </html>
  1. 在 App.java 中运行 main() 方法,这时候再访问 hello.html 会被跳转到 login.html, 使用 userpassword分别作为用户名密码登陆后才可以正常访问。

安全模块的关键在于 WebSecurityConfig.java 类的配置,@EnableWebSecurity 注解会启动Spring的安全模块,并提供对SpringMVC的支持。configure(HttpSecurity) 指定了那些页面应该被保护那些不是,并指定了登陆/登出页面。configureGlobal(AuthenticationManagerBuilder)方法在内存中创建了一个授权用户/密码。

当用户访问了授权页面时,会被重定向到 login.html,它提供了表单入口,用户填写用户名/密码后会发送POST请求给 /login ,Spring会自动处理对于 /login的POST请求,并返回是否登陆成功。

使用UserDetailsService

在Spring安全模块中,包含一个基本的概念 – 用户和赋予的权利。在前一节中,通过

auth
  .inMemoryAuthentication()
     .withUser("user").password("password").roles("USER");

在内存中创建了一个用户。但是对于大部分应用来说都会有自己的用户系统,Spring通过UserDetailsService方便开发者定义自己的身份验证逻辑。UserDetailsService的职责非常简单:给一个用户名,返回一个UserDetails 实现,UserDetails会回答这些问题:用户的合法性、用户名/密码、用户的权利(org.springframework.security.core.GrantedAuthority),接下来的事情继续交给SpringBoot处理。

为了演示UserDetailsService,需要创建一个用户(Account)对象和一段存储逻辑,以便把用户信息持久化。

// Account.java 用户模型
@Entity
public class Account {
    @Id
    @GeneratedValue
    private Long id;

    @JsonIgnore
    private String password;
    private String username;

    public Account(){}

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

// AccountRepository.java 存储用户信息的Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
    Account findByUsername(String name);
}

重新实现WebSecurityConfig.java中的身份信息,在这里通过 AccountRepository 读取保存在数据库中的用户信息来验证用户.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AccountRepository accountRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ....
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)  throws Exception {
        auth.userDetailsService(buildUserDetailsService());
    }

    @Bean
    UserDetailsService buildUserDetailsService() {
        return username -> {
            Account account = accountRepository.findByUsername(username);
            User user = new User(account.getUsername(), account.getPassword(),
                    true, true, true, true,
                    AuthorityUtils.createAuthorityList("USER", "write"));
            return user;
        };
    }
}

这里的关键在于添加了自己的 UserDetailsService 实现,并在这里赋予用户的权利。接下来需要在数据库中临时添加一些用户数据:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
    // 伪造一些数据
    @Bean
    CommandLineRunner init(AccountRepository accountRepository) {
        return (args) -> {
            List<String> names = Arrays.asList(
                    "ntop,lilei,hanmeimei".split(","));
            for(String name: names) {
                accountRepository.save(new Account(name, "password"));
            }
        };
    }
}

接下来可以运行main()方法,尝试用 “ntop:password” 登陆网页了。

    原文作者:ntop
    原文地址: https://www.jianshu.com/p/f99ee4e3ca4a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞