1.导入依赖
<!--导入jwt,是因为需要jwt做token字符串-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
2.配置文件
配置文件分为资源配置和授权服务主配置文件,其他配置文件为:token配置,管理认证器配置。原理即为必须进行授权认证通过,才能进行资源访问,否则则会被拦截。
2.1资源文件配置
* 资源配置文件
*
* @author zzp
* @version 1.0
**/
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "admin";
@Autowired
TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(RESOURCE_ID)//资源 id--需要和数据库中的资源id一样
.tokenStore(tokenStore)
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
//配置通过路径
http
.authorizeRequests()
.antMatchers("/*/**", "/api/user/login","/*/remindTemp/remote/selectTemp","/api/user/msg/**",
"/**/v2/api-docs/**", "/swagger-resources/configuration/ui",
"/swagger-resources", "/swagger-resources/configuration/security",
"/swagger-ui.html").permitAll()
.antMatchers("/**")
.access("#oauth2.hasScope('ROLE_ADMIN')")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
2.2 token文件配置
* token配置文件
* @author zzp
* @version 1.0
**/
@Configuration
public class TokenConfig {
private String SIGNING_KEY = "hr2020";
@Bean
public TokenStore tokenStore() {
//JWT令牌存储方案
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
}
2.3 认证管理器配置
/**
* @author Administrator
* @version 1.0
**/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebAuthSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
//认证管理器
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/user/login").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
;
}
}
2.4 授权服务配置
* @author ZZP
* @version 1.0
* 授权服务配置
**/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
private WebResponseExceptionTranslator webResponseExceptionTranslator;
//将客户端信息存储到数据库
@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
return clientDetailsService;
}
//客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService);
}
//令牌管理服务
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);//客户端详情服务
//service.setSupportRefreshToken(true);//支持刷新令牌
//service.setReuseRefreshToken(true); // 复用refresh token
service.setTokenStore(tokenStore);//令牌存储策略
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(60 * 60 * 7); // TODO 需要修改 令牌默认有效期2小时 60*60*2----1*60
service.setRefreshTokenValiditySeconds(60 * 60 * 7); // 刷新令牌默认有效期3天
return service;
}
//设置授权码模式的授权码如何存取,采用数据库
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)//认证管理器
.authorizationCodeServices(authorizationCodeServices)//授权码服务
.tokenServices(tokenService())//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
.exceptionTranslator(webResponseExceptionTranslator);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token公开
.allowFormAuthenticationForClients() //表单认证(申请令牌)
;
}
}
3.获取token数据
//实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginEntity {
@ApiModelProperty(value = "密码")
@NotBlank(message = "密码不能为空")
private String password;
@ApiModelProperty(value = "用户名")
@NotBlank(message = "用户名不能为空")
private String userName;
@ApiModelProperty(value = "登录类型:默认传值(authorization_code-授权码,password-密码,refresh_token-刷新token)")
@NotBlank(message = "登录类型不能为空")
private String grantType;
}
/**
* 获取客户端信息,前端传入请求头参数为:curl_client:secret的base64,即Y3VybF9jbGllbnQ6c2VjcmV0
*
* @return
*/
public String[] extractHeaderClient() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
String header = request.getHeader("Authorization");
if (StringUtils.isBlank(header)) {
throw new RuntimeException("请输入正确的客户端信息");
}
byte[] base64Client = header.substring(BASIC_.length()).getBytes(StandardCharsets.UTF_8);
byte[] decoded = Base64.getDecoder().decode(base64Client);
String clientStr = new String(decoded, StandardCharsets.UTF_8);
String[] clientArr = clientStr.split(":");
if (clientArr.length != 2) {
throw new RuntimeException("请输入正确的客户端信息");
}
return clientArr;
}
/**
* 获取登录码
*
* @param login
* @return
*/
private OAuth2AccessToken httpLogin(LoginEntity login) {
String applicationName = "localhost:" + port;
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
if (!"refresh_token".equals(login.getGrantType())) {
paramsMap.set("username", login.getUserName());
paramsMap.set("password", login.getPassword());
}
String[] clientInfos = loginUserHolder.extractHeaderClient();
String clientId = clientInfos[0];
String clientSecret = clientInfos[1];
paramsMap.set("client_id", clientId);
paramsMap.set("grant_type", login.getGrantType());
paramsMap.set("client_secret", clientSecret);
// 构造头部信息
HttpHeaders headers = new HttpHeaders();
// 设置类型 "application/json;charset=UTF-8"
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> req = new HttpEntity(paramsMap, headers);
//HTTP 请求登录接口
String requestUrl = "http://" + applicationName + "/oauth/token";
try {
OAuth2AccessToken result = restTemplate.postForObject(requestUrl, req, OAuth2AccessToken.class, paramsMap);
return result;
} catch (Exception e) {
log.error("登录失败");
e.printStackTrace();
}
return null;
}
//调用方法获取token
OAuth2AccessToken oAuth2AccessToken = this.httpLogin(loginEntity);
Map<String, String> reslut = new HashMap();
reslut.put("token", oAuth2AccessToken.getValue());
4.备注
需要在数据库中创建表存储客户端信息
create table oauth_client_details
(
client_id varchar(36) not null comment '客户端id',
resource_ids varchar(255) null comment '资源id',
client_secret varchar(255) null comment '客户端密钥',
scope varchar(255) null,
authorized_grant_types varchar(255) null,
web_server_redirect_uri varchar(255) null,
authorities varchar(255) null,
access_token_validity int null,
refresh_token_validity int null,
additional_information varchar(255) null,
autoapprove varchar(255) null
)
INSERT INTO hdhr.oauth_client_details (client_id, resource_ids, client_secret, scope, authorized_grant_types,
web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove)
VALUES ('curl_client', 'admin', '$2a$10$XuKyPx8tQFAuDcjQqPE8w.N/3vaLm/UWSn6LAZ5SFJUrT8wckXCem', 'ROLE_ADMIN', 'password',
null, null, null, null, null, null);