又是折腾自己的一天,明明可以靠脸吃饭的我,偏偏选择了靠技术
废话不多说让我们开始进入正文吧
首先我们要先配置一下security
Security 配置
- 导入maven依赖
<!--security oauth2安全依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
- 自定义security配置类WebSecurityConfig继承WebSecurityConfigurerAdapter
- 重写configure(HttpSecurity http)方法,定义拦截规则
- 编写loginUser继承User
- 编写LwUserDetailsService继承UserDetailsService,创建LwUserDetailsServiceImpl实现 LwUserDetailsService方法,用于加载用户信息以及用户资源权限
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义鉴权规则
* @param http
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //token方式 关闭csrf
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//token 将session设置成无状态
.and()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll() //登录接口放开鉴权
.anyRequest().authenticated()
.and()
.cors();
}
/**
* 将 AuthenticationManager 注入到spring容器中
* AuthenticationManager默认是不对外开放得
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* LwUserDetailsService继承UserDetailsService
* 负责用户信息以及权限信息查询
*/
@Bean
public LwUserDetailsService userDetailsService() {
return new LwUserDetailsServiceImpl();
}
/**
* 设置密码加密方式
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public class LoginUser extends User {
@Getter
@Setter
private String userId;
@Getter
@Setter
private String username;
@Getter
@Setter
private String password;
@Getter
@Setter
private String phone;
@Getter
@Setter
private String avatar;
@Getter
@Setter
private String openid;
public LoginUser(String username, String password, String userId, String phone, String avatar, String openid, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
this.username = username;
this.userId = userId;
this.password = password;
this.phone = phone;
this.avatar = avatar;
this.openid = openid;
}
}
public class LwUserDetailsServiceImpl implements LwUserDetailsService {
@Resource
private RemoteUserService remoteUserService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
R<SysUser> sysUserR = remoteUserService.getUserInfo(username, SecurityConstants.FROM_IN);
if (!sysUserR.isOk()) {
throw new UsernameNotFoundException("账号或密码错误");
}
SysUser sysUser = sysUserR.getData();
//TODO 获取资源权限注入到userdetails中----待完善
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList("admin");
LoginUser user = new LoginUser(sysUser.getUsername(), sysUser.getPassword(),
sysUser.getUserId(), sysUser.getPhone(), sysUser.getAvatar(), sysUser.getOpenid(), authorities);
return user;
}
}
以上主要是配置security的鉴权、自定义用户加载方式、密码加密方式以及开放AuthenticationManager对象
接下来我们要做的就是配置认证服务器
认证服务器配置
配置认证服务器我们需要做的事分为以下几点
- 获取客户端信息,验证客户端是否有权限访问认证服务器,我们可以通过jdbc、内存等方式获取
- 配置令牌服务,设置令牌存储策略
- 配置令牌端点访问规则,以及配置令牌端点安全约束
/***
*
* 客户端配置
* @param clients
*/
//因为我这边数据库表名和默认标识不同,所以我需要自定义sql语句
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
jdbcClientDetailsService.setSelectClientDetailsSql(SecurityConstants.SELECT_CLIENT_DETAILS_SQL);
clients.withClientDetails(jdbcClientDetailsService);
}
/**
* 查询客户端sql
*/
String SELECT_CLIENT_DETAILS_SQL = "select client_id,client_secret, resource_ids, scope, "
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
+ "refresh_token_validity, additional_information, autoapprove"
+ " from sys_oauth_client_details where client_id = ?";
/**
* 令牌服务
*
* @param
*/
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
services.setSupportRefreshToken(true);//启用刷新token
services.setClientDetailsService(clientDetailsService); //自定义客户端加载方式
services.setAuthenticationManager(authenticationManagerBean);//注入认证管理器,这样才可以兼容4中授权模式
services.setTokenStore(tokenStore());//设置token存储策略
//这一段因项目而异,我采用的是JWT token
TokenEnhancerChain chain = new TokenEnhancerChain();
chain.setTokenEnhancers(Arrays.asList(lwTokenEnhancer, jwtAccessTokenConverter));//采用JWTtoken方式增强
services.setTokenEnhancer(chain);//设置token增强器
return services;
}
/**
* 存储策略
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* jwt转换器
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
DefaultAccessTokenConverter defaultConverter = new DefaultAccessTokenConverter();
defaultConverter.setUserTokenConverter(lwUserAuthenticationConverter());
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(defaultConverter);
converter.setSigningKey("123456");
return converter;
}
/**
* 令牌访问端点配置
*
* @param endpoints
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
.authenticationManager(authenticationManagerBean)
.tokenServices(tokenServices())
.exceptionTranslator(new LwWebResponseExceptionTranslator())
.tokenStore(tokenStore())
.userDetailsService(lwUserDetailsService);
}
/**
* 端点约束
*
* @param security
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients().checkTokenAccess("isAuthenticated()");
}
不要忘记在配置类上加上@EnableAuthorizationServer注解
效果图
认证服务器默认情况下是不会返回用户信息的,也就说userInfo中的信息,是我通过往tokenservice中注入了lwTokenEnhancer增强方案,加入了userInfo后才返回的,具体怎么自定义返回信息可以看看spring cloud oauth 集成 JWT 之 token 增强