Spring Security 系列教程(2)

来源:互联网 发布:农产品网络销售 编辑:程序博客网 时间:2024/05/19 06:14

从本篇开始,将逐步介绍Spring Security的特性。阅读本篇教程之前,需要对 Spring Data JPA有一定了解。

本次教程,我们将实现从数据库读取用户认证以及权限信息

本次教程,将使用到以下的框架(以后的教程,都只会列出新增的框架,之前已经列出的,将不再列出):

  • lombok 通过注解方式即可生成Java bean的 getter/setter/builder/constructor 等,具体请参考官网lombok官网
  • Spring Data JPA 官方文档
  • Swagger 用于生成、描述、调用和可视化 RESTful 风格的API

首先,我们先将本节依赖的环境搭建好。

添加新增的maven依赖

spring data jpa 依赖:

<!--data--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId></dependency>

lombok 依赖:

<dependency>    <groupId>org.projectlombok</groupId>    <artifactId>lombok</artifactId>    <version>1.16.16</version></dependency>

注:除了添加lombok依赖之外,还需安装IDE插件,如果使用的是idea,直接搜索lombok插件并安装即可

swagger 依赖:

<dependency>    <groupId>io.springfox</groupId>    <artifactId>springfox-swagger2</artifactId>    <version>2.7.0</version></dependency><dependency>    <groupId>io.springfox</groupId>    <artifactId>springfox-swagger-ui</artifactId>    <version>2.7.0</version></dependency>

配置数据源

#禁用http basic认证security.basic.enabled = falsespring.datasource.url=jdbc:mysql://localhost/spring_securityspring.datasource.username=spring.datasource.password=spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.jpa.hibernate.ddl-auto=updatespring.jpa.properties.hibernate.format_sql=truespring.jpa.open-in-view=truespring.jpa.show-sql=true

在本教程中,将使用MySQL数据库,我们需要手动在MySQL中建立数据库,请按照自己机器的设置配置此处的数据源信息

添加swagger配置类

package me.learningai.config;import io.swagger.annotations.ApiOperation;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.service.ApiInfo;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.time.LocalDate;/** * @author heyx */@Configuration@EnableSwagger2public class Swagger2Config {    private static final String VERSION = "1.0";    @Bean    public Docket apiDocket() {        return new Docket(DocumentationType.SWAGGER_2)            .select()            //配置swagger 处理所有添加了 @ApiOperation的方法,用以生成文档            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))            .build()            .directModelSubstitute(LocalDate.class, java.sql.Date.class)            .directModelSubstitute(LocalDate.class, java.util.Date.class)            .apiInfo(apiInfo());    }    private ApiInfo apiInfo() {        return new ApiInfoBuilder()            .title("Swagger API")            .description("base java ee framework")            .license("Apache 2.0")            .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")            .version(VERSION)            .build();    }}

此处仅仅做了最简单的配置,有关swagger其他设置,请参考: 官方文档

最后,我们需要修改 Spring Security设置,允许匿名 Swagger 文档:

WebSecurityConfig.java

    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .csrf().disable()            .authorizeRequests()            //允许swagger 文档匿名访问            .antMatchers("/swagger*/**","/v2/**", "/webjars/**").permitAll()            //设置其他所有请求都需认证            .anyRequest().authenticated()            .and()            .formLogin().defaultSuccessUrl("/user/me");    }

至此,所需环境以搭建完毕,运行程序并访问 http://localhost:8080/swagger-ui.html ,我们将看到以下的界面:
这里写图片描述

现在,我们将正式开始实现从数据库读取用户认证信息。

第一步,我们先来看一下数据库定义:

这里写图片描述

表结构非常简单,一张用户表,一张权限表以及它们之间的关联表。

关于表的entity/repository/service就不贴出来了,具体实现,请查看源码。

下面,我们需要自定义class 并实现 UserDetailsService 接口,UserDetailsService是Spring security加载用户信息的入口,里面只有一个方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

这个方法返回了 UserDetails 用于提供给Spring security 进行用户权限的判断。

SpringDataUserDetailsService:

package me.learningai.security.core;import me.learningai.security.entity.User;import me.learningai.security.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;/** * @author heyx */public class SpringDataUserDetailsService implements UserDetailsService {    private UserService userService;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        User user = userService.findByUsername(username);        if (user == null) {            throw new UsernameNotFoundException("username:" + username + " not found");        }        return new AuthorityUser(user);    }    @Autowired    public void setUserService(UserService userService) {        this.userService = userService;    }}

这里只是对UserDetailsService做了很简单的实现,通过用户名从 UserService 加载了用户信息,并转换成了 AuthorityUser了,AuthorityUser 实现了 UserDetails 接口。

最后,我们还需修改一下Spring security的配置,让它能从我们自定义的 UserDetailsService 中加载用户信息。

package me.learningai.config;import me.learningai.security.core.SpringDataUserDetailsService;import org.springframework.context.annotation.Bean;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.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/** * spring security 配置. * @author heyx */@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        //设置自定义UserDetailService,用以从数据库加载用户信息        auth.userDetailsService(springDataUserDetailsService())            //设置密码加密            .passwordEncoder(passwordEncoder());    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .csrf().disable()            .authorizeRequests()            //允许swagger 文档匿名访问            .antMatchers("/swagger*/**","/v2/**", "/webjars/**").permitAll()            //设置其他所有请求都需认证            .anyRequest().authenticated()            .and()            .formLogin().defaultSuccessUrl("/user/me");    }    @Bean    public SpringDataUserDetailsService springDataUserDetailsService() {        return new SpringDataUserDetailsService();    }    @Bean    public BCryptPasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder(8);    }}

此处定义了用户密码加密策略,有需要可自行实现 PasswordEncoder 接口并修改配置。

其他接口:

API Method 描述 /v1/user POST 创建用户 /v1/user/me GET 获取当前登录用户 /v1/authority POST 创建Authority

具体参数,可参考 swagger 文档,http://localhost:8080/swagger-ui.html

现在,让我们启动应用,并访问 http://localhost:8080/user/me,因为没有认证,所以会跳转到登陆页面去,在登陆页面输入用户名密码(预置数据,用户名:user,密码:123456),点击登陆后,我们就会跳转到 http://localhost:8080/v1/user/me页面获取当前登录用户的信息了:

这里写图片描述

注:本教程提供的都是 rest 接口,为了简便起见,还是使用了Spring security 的formlogin 方式进行登陆,关于这个问题,后面会专门写一篇博客介绍编写无状态的 rest接口,在那里,会提供这个问题的解决办法。

好了,本次教程也到此结束了,因为版面的原因,controller/service/repository都没有直接贴代码,有需要的可直接获取:源码

下一篇教程,会介绍Spring security 如何结合Spring session。