废话刷说,这个问题已经困扰我多时,我已经经过n次尝试,n-1都是失败的,最后只有一次成功,失败是成功他妈,是血的教训,佛说千万次错过就是为了那一次邂逅。
先把我遇到的问题罗列出来:
(1)资源服务器和认证服务器分离后,配置resource.id无效;
(2)存储的secret和用户密码都必须BCEncrypt加密;
(3)认证服务器同时也是资源服务器的时候,四种授权模式不能共生(我出现的是添加上资源服务器,授权码模式无效,implicit简单模式,password模式和client credentials模式有效;认证服务器不做为资源服务器的时候四种授权模式都可以生效)。
(4)Eureka注册服务,如果配置使用了Security,并配置HttpBasic登录模式后,可能与资源服务器的授权模式会冲突(最终注册服务器使用无需用户名和密码模式)。
父工程POM文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.donwaitgroupId>
<artifactId>DYSartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>道鹰4.0name>
<description>道鹰4.0后台集群服务description>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.4.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Finchley.RELEASEspring-cloud.version>
<lombok.version>1.16.20lombok.version>
<docker.registry>localhost:5000docker.registry>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
dependencies>
dependencyManagement>
<repositories>
<repository>
<id>spring-snapshotsid>
<name>Spring Snapshotsname>
<url>https://repo.spring.io/snapshoturl>
<snapshots>
<enabled>trueenabled>
snapshots>
repository>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>com.spotifygroupId>
<artifactId>docker-maven-pluginartifactId>
<version>0.4.13version>
<configuration>
<imageName>${project.name}:${project.version}imageName>
<dockerDirectory>${project.basedir}/src/main/dockerdockerDirectory>
<skipDockerBuild>falseskipDockerBuild>
<resources>
<resource>
<targetPath>/ROOTtargetPath>
<directory>${project.build.directory}directory>
<include>${project.build.finalName}.jarinclude>
resource>
resources>
configuration>
plugin>
plugins>
build>
<modules>
<module>dys_commonmodule>
<module>dys_zipkin_servermodule>
<module>dys_config_centermodule>
<module>dys_register_servermodule>
<module>dys_config_servermodule>
<module>dys_gateway_servermodule>
<module>dys_auth_centermodule>
<module>dys_car_servermodule>
modules>
project>
一、认证服务器
(1)POM配置文件配置
(2)认证服务器配置
package com.donwait.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import com.donwait.redis.MyRedisTokenStore;
import com.donwait.service.impl.UserDetailsServiceImpl;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
@Primary
MyRedisTokenStore redisTokenStore(){
return new MyRedisTokenStore(redisConnectionFactory);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetails());
}
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(redisTokenStore())
.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager);
endpoints.tokenServices(defaultTokenServices());
}
@Primary
@Bean
public DefaultTokenServices defaultTokenServices(){
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(redisTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetails());
tokenServices.setAccessTokenValiditySeconds(60*60*12); // token有效期自定义设置,默认12小时
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7); //默认30天,这里修改
return tokenServices;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()");
security .checkTokenAccess("isAuthenticated()");
security.allowFormAuthenticationForClients();
}
}
(3)Security资源保护配置
package com.donwait.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.donwait.service.impl.UserDetailsServiceImpl;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favor.ioc");
}
}
(4)配置文件配置
server:
port: 7006
spring:
application:
name: auth
datasource:
name: store_service
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/dys_auth_center?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: root
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
validation-query-timeout: 6
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
filters: stat
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: com.donwait.model
eureka:
instance:
prefer-ip-address: true
registry-fetch-interval-seconds: 30
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30
client:
service-url:
defaultZone: http://127.0.0.1:7002/eureka/,http://127.0.0.1:7003/eureka/
endpoints:
health:
sensitive: false
enabled: false
shutdown:
enabled: true
path: /shutdown
sensitive: true
management:
security:
enabled: false
security:
oauth2:
resource:
filter-order: 3
logging:
config: classpath:logback.xml
level:
org:
springframework:
web: INFO
security: DEBUG
#actuator:
info:
author:
name: 李祥祥
email:[email protected]
hostory:
- date: 2018-08-28 10:10:10
user:[email protected]
- date: 2018-07-10 08:30:00
user:[email protected]
build:
artifact: "@project.artifactId@"
name: "@project.name@"
version: "@project.version@"
(5)启动入口
package com.donwait;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* http://localhost:7006/oauth/authorize?response_type=code&client_id=wx_takeout_client_id&redirect_uri=http://localhost:7010/uaa/login
* @author Administrator
*/
@SpringBootApplication // SpringBoot应用
@EnableDiscoveryClient // 开启服务发现功能
@MapperScan("com.donwait.mapper") // 将项目中对应的mapper类的路径加进来就可以了
public class AuthServerApp {
public static void main(String[] args) {;
SpringApplication.run(AuthServerApp.class, args);
}
}
(6)redis配置
package com.donwait.redis;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
public class MyRedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
public MyRedisTokenStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
private byte[] serialize(Object object) {
return this.serializationStrategy.serialize(object);
}
private byte[] serializeKey(String object) {
return this.serialize(this.prefix + object);
}
private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
}
private String deserializeString(byte[] bytes) {
return this.serializationStrategy.deserializeString(bytes);
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? "": authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
private RedisConnection getConnection() {
return this.connectionFactory.getConnection();
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = this.serializeKey(AUTH_TO_ACCESS + key);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(serializedKey);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
if (storedAuthentication == null || !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
this.storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return this.readAuthentication(token.getValue());
}
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(this.serializeKey("auth:" + token));
} finally {
conn.close();
}
OAuth2Authentication auth = this.deserializeAuthentication(bytes);
return auth;
}
@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return this.readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = getConnection();
try {
byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
} finally {
conn.close();
}
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] authKey = serializeKey(AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
RedisConnection conn = getConnection();
try {
conn.openPipeline();
conn.stringCommands().set(accessKey, serializedAccessToken);
conn.stringCommands().set(authKey, serializedAuth);
conn.stringCommands().set(authToAccessKey, serializedAccessToken);
if (!authentication.isClientOnly()) {
conn.rPush(approvalKey, serializedAccessToken);
}
conn.rPush(clientId, serializedAccessToken);
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, seconds);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
conn.expire(approvalKey, seconds);
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] refresh = serialize(token.getRefreshToken().getValue());
byte[] auth = serialize(token.getValue());
byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
conn.stringCommands().set(refreshToAccessKey, auth);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue());
conn.stringCommands().set(accessToRefreshKey, refresh);
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
.intValue();
conn.expire(refreshToAccessKey, seconds);
conn.expire(accessToRefreshKey, seconds);
}
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
@Override
public void removeAccessToken(OAuth2AccessToken accessToken) {
this.removeAccessToken(accessToken.getValue());
}
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = serializeKey(ACCESS + tokenValue);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
return accessToken;
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] authKey = serializeKey(AUTH + tokenValue);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
RedisConnection conn = getConnection();
try {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
conn.del(accessKey);
conn.del(accessToRefreshKey);
// Don't remove the refresh token - it's up to the caller to do that
conn.del(authKey);
List
(7)redis密码比对
package com.donwait.redis;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* PasswordEncoder密码验证clientId的时候会报错,因为5.0新特性中需要在密码前方需要加上{Xxx}来判别。
* 所以需要自定义一个类,重新BCryptPasswordEncoder的match方法
* MyBCryptPasswordEncoder类是我自定义的一个类,用来重新match方法
* @author Administrator
*/
public class MyBCryptPasswordEncoder extends BCryptPasswordEncoder {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String presentedPassword = passwordEncoder.encode(rawPassword);
return passwordEncoder.matches(rawPassword, presentedPassword);
}
}
(8)UserDetailsService接口实现
package com.donwait.service.impl;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 com.donwait.model.Right;
import com.donwait.service.RightService;
import com.donwait.service.UserService;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService; // 用户服务
@Autowired
private RightService rightService; // 权限服务
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
// 查找用户
com.donwait.model.User user = userService.findByUsername(userName);
if (null == user) {
throw new UsernameNotFoundException("用户:" + userName + ",不存在!");
}
// 设置用户权限
Set grantedAuthorities = new HashSet();
List rights = rightService.findByUsername(userName);
for(Right right : rights) {
GrantedAuthority authority = new SimpleGrantedAuthority(right.getName());
grantedAuthorities.add(authority);
}
// 标识位设置
boolean enabled = true; // 可用性 :true:可用 false:不可用
boolean accountNonExpired = true; // 过期性 :true:没过期 false:过期
boolean credentialsNonExpired = true; // 有效性 :true:凭证有效 false:凭证无效
boolean accountNonLocked = true; // 锁定性 :true:未锁定 false:已锁定
return new User(user.getUsername(), user.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);
}
}
(9)注销token配置
package com.donwait.endpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@FrameworkEndpoint
public class RevokeTokenEndpoint {
@Autowired
private ConsumerTokenServices consumerTokenServices;
@RequestMapping(value = "/oauth/token", method= RequestMethod.DELETE)
public @ResponseBody
String revokeToken(String access_token){
String msg = "";
if (consumerTokenServices.revokeToken(access_token)){
msg = "注销成功";
}else {
msg = "注销失败";
}
return msg;
}
}
二、资源服务器配置
(1)POM配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<artifactId>dys_car_serverartifactId>
<name>车辆服务name>
<description>提供平台车辆管理、车牌报警等相关服务description>
<parent>
<groupId>com.donwaitgroupId>
<artifactId>DYSartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>com.donwaitgroupId>
<artifactId>dys_commonartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.9version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>com.google.protobufgroupId>
<artifactId>protobuf-javaartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.security.oauth.bootgroupId>
<artifactId>spring-security-oauth2-autoconfigureartifactId>
dependency>
dependencies>
project>
(2)资源服务器配置
package com.donwait.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// 改资源服务器id必须在数据库记录中有配置,也就是对应token的用户必须该资源访问权限(密文:test_resource_secret)
// 例如,我的数据库记录:'wx_takeout_client_id','test_resource_id','$2a$10$I28j9B0T/roapkMEqfIHguARt0GgLyXwC/DOnFwPpXuQ0xTkrd632','user_info','authorization_code,refresh_token,implicit,password','http://localhost:7010/uaa/login','ROLE_ADMIN,ROLE_DEVICE,ROLE_VIDEO',3600,7200,'{\"systemInfo\":\"Atlas System\"}','true'
// 通过授权模式或简化模式获取的token(对应用户为wx_takeout_client_id)具有访问资源服务器test_resource_id的权限,所以将改资源服务器id要与数据库的对应,否则无法访问
private static final String DEMO_RESOURCE_ID = "test_resource_id";
/**
* 以代码形式配置资源服务器id,配置文件配置不生效
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/add/**")
.authenticated();
}
}
(3)控制器
package com.donwait.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Api(value = "车牌服务控制器", description = "提供车牌抓拍搜索等相关接口")
@RestController
public class CarAlarmController {
@ApiOperation(value="增加车牌抓拍报警", notes="上传车牌识别报警信息并入库")
@RequestMapping("/add/{id}")
public String add(@PathVariable String id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
log.debug("授权信息:{}",authentication);
return "Add car alarm";
}
}
(4)程序入口
package com.donwait;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
@SpringBootApplication
@EnableOAuth2Sso
public class CarApp {
public static void main(String[] args) {
SpringApplication.run(CarApp.class, args);
}
}
三、使用流程
(1)使用授权码模式获取token
打开浏览器,输入:http://localhost:7006/oauth/authorize?response_type=code&client_id=wx_takeout_client_id&redirect_uri=http://localhost:7010/uaa/login
重定向到登录页面:输入用户名密码
登录成功后重定向到redirect_uri并携带了code授权码
使用postman,输入授权码和其他参数,换取token
(2)通过从认证服务器换取的token访问资源服务器
使用GET方式,携带上token授权码,授权码放在header中:
注意:这里必须选择bearer token,这是oauth默认的认证方式,发送后即可返回结果
备注:当然获取token的过程可以使用四种授权模式的任意一种,比如可以使用password模式:
此处的授权认证的username为client_id密码为client_secret, 登录的用户名是lixx,用户名lixx的密码为dw123456
重点: 另外一种配置方式:
security:
oauth2:
resource:
filter-order: 3
id: test_resource_id
tokenInfoUri: http://localhost:7006/oauth/check_token
preferTokenInfo: true
最后两行改为,不使用tokeninfo验证,使用用户信息验证:
user-info-uri:http://localhost:7006/user/principal
prefer-token-info: false
此时资源请求方式为: 直接在url后面带上access_token=xxx, 并且头部不使用授权方式: