这里忽略springcloud-alibaba的搭建,只讲springsecurity的部分。
把生成的密钥对文件放在resources目录下
认证服务器的代码结构图
引入spring-security和oauth2的必须包
2.配置ymlorg.springframework.cloud spring-cloud-starter-security org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.security spring-security-oauth2-jose
严格来说不需要特别配置,这里只讲两个:
①表示后发现的bean会覆盖之前相同名称的bean
spring:
main:
allow-bean-definition-overriding: true
②配置客户端的clientId、clientSecret(非必须),表示这个客户端的标识,资源服务器会根据这个标识去oauth_client_details找其可访问的资源(即resource_ids字段以及scope字段),实际开发中登录时可有哭护短传来。
auth: clientId: c1 clientSecret: secret13.配置类AuthServerConfig
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
//对称密钥------改用非对称密钥
//private static final String SIGNING_KEY = "oauth";
@Resource
private AuthenticationManager authenticationManager;
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private AuthorizationCodeServices authorizationCodeServices;
@Resource
private JwtAccessTokenConverter accessTokenConverter;
@Resource
private TokenStore tokenStore;
@Resource
private ClientDetailsService clientDetailsService;
@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
return clientDetailsService;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
//配置令牌访问端点
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)//认证管理(密码模式需要)
.authorizationCodeServices(authorizationCodeServices)//授权码服务
.tokenServices(tokenServices())//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);//允许post提交访问令牌
}
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setClientDetailsService(clientDetailsService);//客户端详情
tokenServices.setSupportRefreshToken(true);//支持刷新令牌
tokenServices.setTokenStore(tokenStore);//令牌存储策略
//令牌增强 使用jwt令牌
//使用jwt令牌来替代默认令牌,这样做的好处是携带默认令牌访问资源,每次都要通过授权服务来认证令牌是否有效,
// 而jwt则可以做到资源服务中自己解析从而判断令牌的有效性;另外一个优势就是jwt令牌有更高的安全性,
// 可以使用公钥和私钥进行加密和解密,不容易被破解。
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
tokenServices.setTokenEnhancer(tokenEnhancerChain);
tokenServices.setAccessTokenValiditySeconds(7200);//令牌默认有效期2小时
tokenServices.setRefreshTokenValiditySeconds(9600);//刷新令牌默认有效期
return tokenServices;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//加载密钥
converter.setKeyPair(keyPair());
return converter;
}
@Bean
public KeyPair keyPair(){
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("macrosoft.jks"),"420188".toCharArray());
KeyPair keyPair = factory.getKeyPair("macrosoft", "420188".toCharArray());
return keyPair;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")//oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token 已经验证了的客户端才能请求check_token 端点
.allowFormAuthenticationForClients();//表单认证(申请令牌)
}
}
4.配置类SecurityConfig
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//跨域伪造请求无效
.authorizeRequests().antMatchers("
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailServiceImpl();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.GET,
"/favicon.ico",
"*.css",
"*.js");
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5.自定义数据库操作UserDetailServiceImpl
public class UserDetailServiceImpl implements UserDetailsService {
@Resource
private SysUserMapper sysUserMapper;
@Resource
private SysRoleMapper sysRoleMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = null;
try {
//根据username查询用户信息
List sysUserList = sysUserMapper.selectByUserNameSysUsers(username);
if (CollectionUtils.isEmpty(sysUserList)) {
throw new UsernameNotFoundException("该用户不存在!");
}
SysUserEntity sysUserEntity = sysUserList.get(0);
//根据userId查询角色权限
String roles = sysRoleMapper.getRoleNameById(sysUserEntity.getId());
List authorities = new ArrayList<>();
String[] arrstr = roles.split(";");
if (ObjectUtils.isNotEmpty(arrstr)) {
for (String role : arrstr) {
authorities.add(new SimpleGrantedAuthority(role));
}
}
userDetails =
new User(username
, sysUserEntity.getPassword()
, true
, true
, true
, true
, authorities);
} catch (UsernameNotFoundException e) {
e.printStackTrace();
}
return userDetails;
}
}
6.到这里认证的配置就配完了,这里在放一段登录的代码(service)
@Override
public RespResult login(LoginRequestDTO loginRequestDTO) {
RespResult result = new RespResult<>();
ServiceTemplate.executeApiService(getClass(),
AuthScenceEnum.AUTH_LOGIN_SCENCE,
loginRequestDTO,
result,
request -> {
String username = loginRequestDTO.getUsername();
String password = loginRequestDTO.getPassword();
//1.获取当前服务路径
ServiceInstance serviceInstance = loadBalancerClient.choose("macro-auth");
URI uri = serviceInstance.getUri();
String url = uri + "/auth/oauth/token";
//2.组装请求体参数
MultiValueMap body = new linkedMultiValueMap<>();
body.add("grant_type", "password");
body.add("username", username);
body.add("password", password);
//3.在请求头组装clientId和clientSecret
MultiValueMap headers = new linkedMultiValueMap<>();
//注意:重点,客户端在登录时可以传入clientId、clientSecret(线下给到客户端),那么这样就可以在oauth_client_details配置该clientId的访问资源服务器了
//这里目前写死只有一个clientId、clientSecret(即c1和secret1),在oauth_client_details表中配置了c1对应的resource_ids字段的res1,res2,res3
//也就是说资源服务器标识为res1或res2或res3的,clientId为c1的皆可访问
headers.add("Authorization", getHttpBasic(clientId, clientSecret));
//4.组装最终请求参数
HttpEntity> requestEntity = new HttpEntity<>(body, headers);
//5.发起登录请求接口
ResponseEntity 


