微服务集成 SpringSecurity
1.概述
1.1.SpringSecurity-Oauth2介绍
SpingSecurityOauth2实现了Oatuh2,SpingSecurityOauth2分为两个大块,一者为认证授权(Authorization Server)服务和资源服务(Resource server),认证授权服务一般负责执行认证逻辑(登录)和加载用户的权限(给用户授权),以及认证成功后的令牌颁发 ,而资源服务器一般指的是我们系统中的微服务(被访问的微服务),在资源服务器需要对用户的令牌(认证成功与否),以及授权(是不是有访问权限)做检查 。
1.2.Oauth2认证解决方案
这里我们需要准备四个服务,1.授权服务 2.资源服务,3.网关,4注册中心,一共4个服务。授权服务和资源服务的流程图:
注:图片来源于网络
2.环境准备
注意:学习基础 是Springboot
搭建基本项目结构如下
security-parent //父工程
security-auth-server //统一认证微服务
security-eureka-server //注册中心
security-resource-server //资源微服务
security-zuul-server //网关
2.1.搭建父工程
管理SpringBoot和SpringCloud相关依赖,管理公共依赖,以及依赖版本统一管理
1.顶级父模块Pom.xml 引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.eureka 注册中心
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
//启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
3.application.yml:
server:
port: 1000
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false #禁用注册中心向自己注册
fetchRegistry: false #不让注册中心获取服务的注册列表
serviceUrl:
defaultZone: http://localhost:1000/eureka/
#注册中心的注册地址 ,其他微服务需要向这个地址注册
3.zuul 网关微服务
1.pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
2.启动类:
@SpringBootApplication
@EnableZuulProxy
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
3.application.yml:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: zuul-server:2000 #指定服务的id
server:
port: 2000
zuul:
ignored-services: "*" #禁止使用服务名字进行访问
prefix: "/servers" #统一的前缀
routes: #配置路由,指定服务的访问路径
resource1-server: "/resources/**"
spring:
application:
name: zuul-server
4.搭建AuthServer认证授权微服务
认证微服务,实现认证逻辑,用户授权,令牌颁发等
1.pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
2.启动类:
@SpringBootApplication
@MapperScan("com.qx680.mapper")
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class) ;
}
}
3.增加配置类:
@SpringBootApplication
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class) ;
}
}
4.application.yml:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: auth-server:3000 #指定服务的id
server:
port: 3000
mybatis:
mapper-locations: classpath:com/qx680/mapper/*Mapper.xml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: admin
url: jdbc:mysql:///auth-rbac
driver-class-name: com.mysql.jdbc.Driver
application:
name: auth-server
4.搭建ResourceServer资源微服务(准备多个)
1.pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.配置类:
@SpringBootApplication
public class Resource1ServerApplication {
public static void main(String[] args) {
SpringApplication.run(Resource1ServerApplication.class) ;
}
}
3.application.yml:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #注册中心地址
instance:
prefer-ip-address: true #使用ip地址注册
instance-id: resource1-server:4000 #指定服务的id
server:
port: 4000
spring:
application:
name: resource1-server
三.认证服务 AuthServer
1.集成Mybatis(上面已经集成):
略…
2.定义UserDetailsService
复写loadUserByUsername,根据用户名从数据库中获取认证的用户对象,并且加载用户的权限信息,把用户的认证信息和权限信息封装成UserDetaials返回。
配置如下:
package com.qx680.userdetails;
import com.qx680.domain.Permission;
import com.qx680.domain.User;
import com.qx680.mapper.PermissionMapper;
import com.qx680.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.bcrypt.BCrypt;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Component
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private PermissionMapper permissionMapper;
/**
* 加载数据库中的认证的用户的信息:用户名,密码,用户的权限列表
* @param username: 该方法把username传入进来,
我们通过username查询用户的信息(密码,权限列表等)
然后封装成 UserDetails进行返回 ,交给security 。
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//User是security内部的对象,UserDetails的实现类 ,
//用来封装用户的基本信息(用户名,密码,权限列表)
//public User(String username, String password, Collection<? extends GrantedAuthority> authorities)
userFromDB = userMapper.selectByUsername(username);
if(null == userFromDB){
throw new RuntimeException("无效的用户");
}
//模拟存储在数据库的用户的密码:123
//String password = passwordEncoder.encode("123");
String password = userFromDB.getPassword();
//查询用户的权限
List<Permission> permission = permissionMapper.selectPermissionsByUserId(userFromDB.getId());
//用户的权限列表,暂时为空
Collection<GrantedAuthority> authorities = new ArrayList<>();
permission.forEach(e->{
System.out.println("用户:"+userFromDB.getUsername()+" 加载权限:"+e.getExpression());
authorities.add(new SimpleGrantedAuthority(e.getExpression()));
});
org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User (username,password, authorities);
return user;
}
}
认证服务在处理认证逻辑的时候会通过该userDetailService加载用户在数据库中的认证信息实现身份认证,同时在整个方法中也加载了用户的权限列表,后续当用户发起对资源服务的访问时,是否能够授权成功就跟用户的权限列表息息相关了。
3.WebSecurity-web安全配置
配置如下
//Security配置
@Configuration
@EnableWebSecurity(debug = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//密码 编码器
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//授权规则配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //屏蔽跨域防护
.authorizeRequests() //对请求做授权处理
.antMatchers("/login").permitAll() //登录路径放行
.antMatchers("/login.html").permitAll()//对登录页面跳转路径放行
.anyRequest().authenticated() //其他路径都要拦截
.and().formLogin() //允许表单登录, 设置登陆页
.successForwardUrl("/loginSuccess") // 设置登陆成功页(对应//controller),一定要有loginSuccess这个路径
.and().logout().permitAll(); //登出
}
//配置认证管理器,授权模式为“poassword”时会用到
/*
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
*/
}
//注意:loginSuccess需要对应一个controller
@RestController
public class LoginController {
@RequestMapping("/loginSuccess")
public String loginSuccess(){
return "登錄成功";
}
}
测试:http://localhost:3000/login