SpringCloud整合spring security+ oauth2+Redis实现认证授权

设置通用父工程依赖

在微服务构建中,我们一般用一个父工程来通知管理依赖的各种版本号信息。父工程pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>

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">

4.0.0

com.zjq

oauth2-demo

pom

1.0-SNAPSHOT

commons

ms-gateway

ms-oauth2-server

ms-registry

2.3.7.RELEASE

Hoxton.SR9

1.18.16

3.11

2.1.3

8.0.22

2.1.5-RELEASE

5.4.7

20.0

1.8

1.8

UTF-8

org.springframework.boot

spring-boot-dependencies

${spring-boot-version}

pom

import

org.springframework.cloud

spring-cloud-dependencies

${spring-cloud-version}

pom

import

org.projectlombok

lombok

${lombok-version}

org.apache.commons

commons-lang3

${commons-lang-version}

org.mybatis.spring.boot

mybatis-spring-boot-starter

${mybatis-starter-version}

com.battcn

swagger-spring-boot-starter

${swagger-starter-version}

mysql

mysql-connector-java

${mysql-version}

cn.hutool

hutool-all

${hutool-version}

com.google.guava

guava

${guava-version}

org.springframework.boot

spring-boot-maven-plugin


构建eureka注册中心

在SpringCloud微服务体系中服务注册中心是一个必要的存在,通过注册中心提供服务的注册和发现。具体细节可以查看我之前的博客,这里不再赘述。我们开始构建一个eureka注册中心,对应的yml配置文件如下:

server:

port: 8080

spring:

application:

# 应用名称

name: ms-registry

# 配置 Eureka Server 注册中心

eureka:

client:

register-with-eureka: false

fetch-registry: false

service-url:

defaultZone: http://localhost:8080/eureka/

logging:

pattern:

console: '%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'


对应的项目启动类代码如下:

package com.zjq.msregistry;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**

* 注册中心

* @author zjq

*/

//启动 eureka注册中心服务端相关组件

@EnableEurekaServer

@SpringBootApplication

public class MsRegistryApplication {

public static void main(String[] args) {

SpringApplication.run(MsRegistryApplication.class, args);

}

}


至此,一个单体的服务注册中心搭建完成。

构建认证授权服务

上文我们已经完成了注册中心的搭建,接下来我们开始搭建认证授权中心。

配置文件设置

我们同样在父工程下面新建一个子工程,作为认证授权中心的微服务。对应的yml文件和pom文件配置如下:

application.yml

server:

port: 8082 # 端口

spring:

application:

name: ms-oauth2-server # 应用名

# 数据库

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver

username: root

password: 123456

url: jdbc:mysql://127.0.0.1:3306/oauth2?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false

# Redis

redis:

port: 6379

host: localhost

timeout: 3000

database: 1

password: 123456

# swagger

swagger:

base-package: com.zjq.oauth2

title: 认证服务API接口文档

# Oauth2

client:

oauth2:

client-id: appId # 客户端标识 ID

secret: 123456 # 客户端安全码

# 授权类型

grant_types:

- password

- refresh_token

# token 有效时间,单位秒

token-validity-time: 3600

refresh-token-validity-time: 3600

# 客户端访问范围

scopes:

- api

- all

# 配置 Eureka Server 注册中心

eureka:

instance:

prefer-ip-address: true

instance-id: ${spring.cloud.client.ip-address}:${server.port}

client:

service-url:

defaultZone: http://localhost:8080/eureka/

# Mybatis

mybatis:

configuration:

map-underscore-to-camel-case: true # 开启驼峰映射

# 指标监控健康检查

management:

endpoints:

web:

exposure:

include: "*" # 暴露的端点

logging:

pattern:


pom.xml

<?xml version="1.0" encoding="UTF-8"?>

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">

oauth2-demo

com.zjq

1.0-SNAPSHOT

4.0.0

ms-oauth2-server

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-data-redis

org.mybatis.spring.boot

mybatis-spring-boot-starter

mysql

mysql-connector-java

org.springframework.cloud

spring-cloud-starter-security

org.springframework.cloud

spring-cloud-starter-oauth2

com.zjq

commons

1.0-SNAPSHOT

org.springframework.boot

spring-boot-configuration-processor

true


Security配置类

我们开始搭建Spring Security相关的配置类,具体配置类代码如下:

package com.zjq.oauth2.server.config;

import cn.hutool.crypto.digest.DigestUtil;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.security.authentication.AuthenticationManager;

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.password.PasswordEncoder;

import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;

/**

* Security 配置类

* @author zjq

*/

@Configuration

@EnableWebSecurity

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

// 注入 Redis 连接工厂

@Resource

private RedisConnectionFactory redisConnectionFactory;

/**

* 初始化 RedisTokenStore 用于将 token 存储至 Redis

* @return

*/

@Bean

public RedisTokenStore redisTokenStore() {

RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);

redisTokenStore.setPrefix("TOKEN:"); // 设置key的层级前缀,方便查询

return redisTokenStore;

}

// 初始化密码编码器,用 MD5 加密密码

@Bean

public PasswordEncoder passwordEncoder() {

return new PasswordEncoder() {

/**

* 加密

* @param rawPassword 原始密码

* @return

*/

@Override

public String encode(CharSequence rawPassword) {

return DigestUtil.md5Hex(rawPassword.toString());

}

/**

* 校验密码

* @param rawPassword 原始密码

* @param encodedPassword 加密密码

* @return

*/

@Override

public boolean matches(CharSequence rawPassword, String encodedPassword) {

return DigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);

}

};

}

// 初始化认证管理对象

@Bean

@Override

public AuthenticationManager authenticationManagerBean() throws Exception {

return super.authenticationManagerBean();

}

// 放行和认证规则

@Override

protected void configure(HttpSecurity http) throws Exception {

http.csrf().disable()

.authorizeRequests()

// 放行的请求

.antMatchers("/oauth/**", "/actuator/**").permitAll()

.and()

.authorizeRequests()

// 其他请求必须认证才能访问

.anyRequest().authenticated();

}

}


Security配置类主要完成以下配置:

注入 Redis 连接工厂

初始化 RedisTokenStore 用于将 token 存储至 Redis

初始化密码编码器,用 MD5 加密密码

初始化认证管理对象

设置放行和认证规则

授权服务配置类

配置完了security配置类后,我们开始编写授权服务配置类,授权服务配置类需要继承AuthorizationServerConfigurerAdapter并重写对应的方法,tips:idea子类重写父类快捷键是Ctrl+O,重写后的授权服务配置类如下:

package com.zjq.oauth2.server.config;

import com.zjq.commons.model.domain.SignInIdentity;

import com.zjq.oauth2.server.service.UserService;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;

import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;

import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;

import java.util.LinkedHashMap;

/**

* 授权服务配置类

* @author zjq

*/

@Configuration

@EnableAuthorizationServer

public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

// RedisTokenSore

@Resource

private RedisTokenStore redisTokenStore;

// 认证管理对象

@Resource

private AuthenticationManager authenticationManager;

// 密码编码器

@Resource

private PasswordEncoder passwordEncoder;

// 客户端配置类

@Resource

private ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;

// 登录校验

@Resource

private UserService userService;

/**

* 配置令牌端点安全约束

*

* @param security

* @throws Exception

*/

@Override

public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

// 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的

security.tokenKeyAccess("permitAll()")

// 允许检查 token 的状态,默认 /oauth/check_token 是受保护的

.checkTokenAccess("permitAll()");

}

/**

* 客户端配置 - 授权模型

*

* @param clients

* @throws Exception

*/

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId()) // 客户端标识 ID

.secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret())) // 客户端安全码

.authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes()) // 授权类型

.accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime()) // token 有效期

.refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidityTime()) // 刷新 token 的有效期

.scopes(clientOAuth2DataConfiguration.getScopes()); // 客户端访问范围

}

/**

* 配置授权以及令牌的访问端点和令牌服务

*

* @param endpoints

* @throws Exception

*/

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

// 认证器

endpoints.authenticationManager(authenticationManager)

// 具体登录的方法

.userDetailsService(userService)

// token 存储的方式:Redis

.tokenStore(redisTokenStore);

}

}


上面用到的客户端配置类如下:

package com.zjq.oauth2.server.config;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

/**

* 客户端配置类

* @author zjq

*/

@Component

@ConfigurationProperties(prefix = "client.oauth2")

@Data

public class ClientOAuth2DataConfiguration {

// 客户端标识 ID

private String clientId;

// 客户端安全码

private String secret;

// 授权类型

private String[] grantTypes;

// token有效期

private int tokenValidityTime;

/**

* refresh-token有效期

*/

private int refreshTokenValidityTime;

/**

* 客户端访问范围

*/

private String[] scopes;

}


具体登录的方法实现:

登录实现

package com.zjq.oauth2.server.service;

import com.zjq.commons.model.domain.SignInIdentity;

import com.zjq.commons.model.pojo.Users;

import com.zjq.commons.utils.AssertUtil;

import com.zjq.oauth2.server.mapper.UsersMapper;

import org.springframework.beans.BeanUtils;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**

* 登录校验

* @author zjq

*/

@Service

public class UserService implements UserDetailsService {

@Resource

private UsersMapper usersMapper;

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

AssertUtil.isNotEmpty(username, "请输入用户名");

Users users = usersMapper.selectByAccountInfo(username);

if (users == null) {

throw new UsernameNotFoundException("用户名或密码错误,请重新输入");

}

// 初始化登录认证对象

SignInIdentity signInIdentity = new SignInIdentity();

// 拷贝属性

BeanUtils.copyProperties(users, signInIdentity);

return signInIdentity;

}

}


UsersMapper:

package com.zjq.oauth2.server.mapper;

import com.zjq.commons.model.pojo.Users;

import org.apache.ibatis.annotations.Param;

import org.apache.ibatis.annotations.Select;

/**

* 用户 Mapper

* @author zjq

*/

public interface UsersMapper {

/**

*

* 根据用户名 or 手机号 or 邮箱查询用户信息

*

* @param account

* @return

*/

@Select("select id, username, nickname, phone, email, " +

"password, avatar_url, roles, is_valid from t_users where " +

"(username = #{account} or phone = #{account} or email = #{account})")

Users selectByAccountInfo(@Param("account") String account);

}


用户实体:

package com.zjq.commons.model.pojo;

import com.zjq.commons.model.base.BaseModel;

import lombok.Getter;

import lombok.Setter;

/**

* 用户实体类

*

* @Author zjq

* @Date 2022/10/12

*/

@Getter

@Setter

public class Users extends BaseModel {

// 主键

private Integer id;

// 用户名

private String username;

// 昵称

private String nickname;

// 密码

private String password;

// 手机号

private String phone;

// 邮箱

private String email;

// 头像

private String avatarUrl;

// 角色

private String roles;

}


package com.zjq.commons.model.base;

import lombok.Getter;

import lombok.Setter;

import java.io.Serializable;

import java.util.Date;

/**

* 实体对象公共属性

*

* @Author zjq

* @Date 2022/10/12

*/

@Getter

@Setter

public class BaseModel implements Serializable {

private Integer id;

private Date createDate;

private Date updateDate;

private int isValid;

}

到此,我们完成了认证授权服务构建,接下来我们进行测试验证:

测试验证

我们启动注册中心和认证授权微服务。访问注册中心:http://localhost:8080/

可以看到认证授权服务已经注册到注册中心。


接下来我们通过postman访问请求token测试:

接下来我们通过postman访问请求token测试:


Authorization请求头中配置,username和password,对应oauth客户端中的配置:



在body中配置请求参数,发起请求后返回如下:

在Redis中我们也可以看到生成的相关token配置:

至此,我们完成了认证授权中心的初步搭建。

————————————————

版权声明:本文为CSDN博主「共饮一杯无」

原文链接:https://blog.csdn.net/qq_35427589/article/details/127340635

展开阅读全文

页面更新:2024-04-20

标签:令牌   编码器   重写   初始化   客户端   有效期   对象   密码   工程   中心

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top