依赖
注:使用的springboot版本 2.2.4.RELEASE ;spring cloud 版本 Hoxton.SR1
加入oauth2和web相关的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-cloud-starter-oauth2 已经包含了security等相关依赖 只需要导入即可 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
集成步骤
- 继承
WebSecurityConfigurerAdapter
配置security相关配置 - 继承
AuthorizationServerConfigurerAdapter
配置授权服务 - 实现
**UserDetailsService**
,查询客户信息
WebSecurityConfig配置
package com.Lonni.oauth.config;
import cn.hutool.crypto.digest.DigestUtil;
import com.Lonni.oauth.exception.filter.CustomOncePerRequestFilter;
import com.Lonni.oauth.provider.MobileCodeAuthenticationProvider;
import com.Lonni.oauth.service.UserDetailsServiceImpl;
import org.apache.tomcat.util.digester.Digester;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.header.HeaderWriterFilter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 查询客户端是需要用这个加解密 而不是原始的
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置redis
*/
@Autowired
private RedisTemplate redisTemplate;
/**
* 查询用户信息
*/
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 配置密码模式需要的AuthenticationManager
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
/**
* 这里是对认证管理器的添加配置
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//设置查询的userDetailsService
.userDetailsService(userDetailsService)
//设置密码加密器 设置为自定义工具类 而不是使用security自带的
.passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
String mdStr = DigestUtil.sha1Hex((String) charSequence);
return mdStr;
}
@Override
public boolean matches(CharSequence charSequence, String encodedPassword) {
return encodedPassword.equals(DigestUtil.sha1Hex((String) charSequence));
}
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.formLogin()
.and()
.logout();
}
}
.passwordEncoder方法设置在查询用户时密码比对的加密器;可根据自己的需要替换
配置 AuthorizationServer
服务
- 配置jwt token配置类
- 配置jwt token增强器
- 配置server配置
jwt token配置类
package com.Lonni.core.aouth2.config;
import com.Lonni.common.constants.AuthConstant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* @program: Lonni-plus
* @description: jwttoken配置类
* @author: lonni
* @create: 2020-11-19 21:18
*/
@Configuration
public class JwtTokenConfig {
/**
* 1:配置 JWT 数据转换
* 设置解密的key
* @return
*/
@Bean("jwtAccessTokenConverter")
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter=new JwtAccessTokenConverter();
//设置为解密token的key 同样在加密的时候也要设置为这个key
converter.setSigningKey(AuthConstant.AUTH_SECURITY_JWT_SIGN_KEY);
return converter;
}
/**
* 2:返回jwt存储器
* @return
*/
@Bean("jwtTokenStore")
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
}
配置jwt token增强器,在生成token时增加额外信息
package com.Lonni.oauth.config;
import com.Lonni.common.constants.AuthConstant;
import com.Lonni.oauth.model.CustomUserDetails;
import com.Lonni.oauth.utils.PrincipalUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.security.Principal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 增强jwt使用
*/
public class JWTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
//password,mobile,captcha
String grantType = oAuth2Authentication.getOAuth2Request().getGrantType();
//生成jwt时不会生成所有的信息 需要自己存入
if ("password".equals(grantType) || "mobile".equals(grantType) || "captcha".equals(grantType)) {
Object principal = oAuth2Authentication.getPrincipal();
//登录成功后就是cus
CustomUserDetails userDetails = (CustomUserDetails) principal;
Map<String, Object> info = new HashMap<>();
//在这里扩展信息
info.put("userId", userDetails.getUserId());
info.put("userName", userDetails.getUsername());
info.put("nickName", userDetails.getNickName());
info.put("phone", userDetails.getPhone());
info.put("openId", userDetails.getOpenId());
info.put("unionId", userDetails.getUnionId());
info.put("avatar", userDetails.getAvatar());
DefaultOAuth2AccessToken auth2AccessToken = ((DefaultOAuth2AccessToken) oAuth2AccessToken);
auth2AccessToken.setAdditionalInformation(info);
//如果是client_id=app 过时时间为5天
String clientId = oAuth2Authentication.getOAuth2Request().getClientId();
if (AuthConstant.AUTH_APPLICATION_APP_ID.equals(clientId)) {
Calendar calen = Calendar.getInstance();
calen.add(Calendar.DAY_OF_YEAR, 5);
auth2AccessToken.setExpiration(calen.getTime());
}
}
return oAuth2AccessToken;
}
}
注意 : oAuth2Authentication.getPrincipal()如果是授权码模式或则客户端模式获取的时候就不是用户的信息,需要判断授权模式才转换
配置server
package com.Lonni.oauth.config;
import com.Lonni.common.constants.AuthConstant;
import com.Lonni.oauth.exception.filter.CustomAuthenticationEntryPoint;
import com.Lonni.oauth.exception.filter.CustomClientCredentialsTokenEndpointFilter;
import com.Lonni.oauth.granter.TokenGranterExt;
import com.Lonni.oauth.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.TokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
/**
* 配置认证服务文件
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
/**
* AuthenticationManager authenticationManager ;认证管理器;如果为password模式时必须配置
* AuthorizationCodeServices authorizationCodeServices ;如果是授权码模式,必须设置此对象
* ImplicitGrantService implicitGrantService;如果是ImplicitG模式 需要设置此对象
* 访问资源服务器:
* 在header头中加上
* Authorization: Bearer 你的token值
*/
/**
* 如果是密码模式 必须配置
*/
@Autowired
AuthenticationManager authenticationManager;
/**
* 密码加密器 新版本必须配置
*/
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private RedisTemplate redisTemplate;
/***
* 将客户端信息存储到数据库
* 1:配置数据库连接
* 2:注入DataSource
* 3:初始化ClientDetailsService 这里直接在config方法中返回jdbcclient
* 4:修改configure(ClientDetailsServiceConfigurer clients)方法 ,使用数据库方式
* 5:修改授权码实现类 AuthorizationCodeServices 为数据库方式
*
*
*/
@Autowired
private DataSource dataSource;
/**
* 1: 配置客户端的方法
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置客户端存储到db
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
//设置客户端的密码对比加密器
clientDetailsService.setPasswordEncoder(passwordEncoder);
//设置查询的sql
clientDetailsService.setFindClientDetailsSql(AuthConstant.FIND_CLIENT_DETAILS_SQL);
clientDetailsService.setSelectClientDetailsSql(AuthConstant.SELECT_CLIENT_DETAILS_SQL);
clients.withClientDetails(clientDetailsService);
}
// 注入 TokenStore 使用 jwt方式 在jwtTokenConfig中配置了
@Autowired
@Qualifier("jwtTokenStore")
private TokenStore tokenStore;
// 注入 jwt token 转换器 在jwtTokenConfig中配置了
@Autowired
@Qualifier("jwtAccessTokenConverter")
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
public TokenEnhancer jwtTokenEnhancer() {
return new JWTokenEnhancer();
}
/**
* 3 配置令牌服务 不管什么服务 都需要此配置
*
* @return
*/
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//配置客户端信息服务
defaultTokenServices.setClientDetailsService(clientDetailsService);
//配置客户端令牌存储策略
defaultTokenServices.setTokenStore(tokenStore);
//是否产生刷新令牌 如果是jdbc方式 需要在数据库中设置,在这里设置无效
defaultTokenServices.setSupportRefreshToken(true);
//令牌的有效期 2小时
defaultTokenServices.setAccessTokenValiditySeconds(7200);
//刷新令牌的有效期 3天
defaultTokenServices.setRefreshTokenValiditySeconds(259200);
// 配置 token 转换器 jwt方式必须配置
//配置token增加,把一般token转换为jwt token
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancerList = new ArrayList<>();
enhancerList.add(jwtTokenEnhancer());
enhancerList.add(jwtAccessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(enhancerList);
defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
return defaultTokenServices;
}
/**
* 4配置授权服务器的终结点和其他属性
* 令牌访问端点
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
//设置密码模式认证器
.authenticationManager(authenticationManager)
//设置授权码模式认证器
.authorizationCodeServices(this.authorizationCodeServices())
//设置令牌策略
.tokenServices(tokenServices())
//设置查询用户的userDetilService
.userDetailsService(userDetailsService)
//允许post get提交认证
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
;
}
/**
* 配置授权码服务的实现类 数据库方式
*
* @return
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
/**
* 5令牌访问的安全策略
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 即clientId和clientSecret组合起来base加密后放在http header中传递
// .allowFormAuthenticationForClients()
//oauth/token_key是公开
.tokenKeyAccess("permitAll()")
//oauth/check_token公开
.checkTokenAccess("permitAll()")
;
}
}