您的位置:首页 > 编程语言 > Java开发

Spring Security 系列教程(2) - JDBC Authentication

2017-06-25 19:49 148 查看
从本篇开始,将逐步介绍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 = false
spring.datasource.url=jdbc:mysql://localhost/spring_security
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=true
spring.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
@EnableSwagger2
public 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
*/
@EnableWebSecurity
public 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 接口并修改配置。

其他接口:

APIMethod描述
/v1/userPOST创建用户
/v1/user/meGET获取当前登录用户
/v1/authorityPOST创建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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息