微服务登录问题:
解决方式:
我们使用客户端Token+oauth2+jwt+springsecurity
oauth2:简易,开放,安全,不接触账号密码
oauth2四种模式:
@Component
public class MyUserDetailService implements UserDetailsService {
@Autowired
private MyUserMapper myUserMapper;
@Autowired
private PermissionMapper permissionMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//通过username查询
MyUser myUser = myUserMapper.loadByUsername(username);
if(myUser==null){
System.out.println(username + "用户不存在");
}
//通过用户查询所拥有的权限
List<Permission> permissions = permissionMapper.loadByMyUserId(myUser.getId());
List<SimpleGrantedAuthority> authorities=new ArrayList<>();
permissions.forEach(permission -> {
authorities.add(new SimpleGrantedAuthority(permission.getExpression()));
});
//转成需要的数组
//参数需要 String username, String password, Collection<? extends GrantedAuthority> authorities
User user=new User(myUser.getUsername(),myUser.getPassword(),authorities);
return user;
}
}
/**
* 对security安全配置
*/
@Configuration//相当于在xml 里面配置了bean
@EnableWebSecurity(debug = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean //密码模式用到
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
//配置认证管理器,授权模式为“poassword”时会用到
//配置服务详情
@Bean
public ClientDetailsService clientDetailsService(){
return new InMemoryClientDetailsService();
}
//配置一个编码器
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//配置规则
@Override
public void configure(HttpSecurity http ) throws Exception {
http.csrf().disable()//屏蔽跨域处理
.authorizeRequests()//对请求做处理
.antMatchers("/login").permitAll()//对登录放行
.anyRequest().authenticated()//其他页面都要拦截
.and().formLogin()//允许进行表单登录
.successForwardUrl("/loginSuccess")//登录成功跳转
.and().logout().permitAll();//登出放行
}
}
package cn.itsource.wyj.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableAuthorizationServer
public class ClientDetailsServiceConfig extends AuthorizationServerConfigurerAdapter {
//一、配置客户端的参数
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//添加客户信息
clients.inMemory() //http://localhost:4000/oauth/authorize?client_id=www&response_type=code&redirect_uri=http://www.baidu.com
.withClient("www")//添加客户端id
.secret(passwordEncoder.encode("wyj"))//添加客户端密钥 加密
.resourceIds("itsource")//添加资源id
.authorizedGrantTypes("client_credentials",
"password","authorization_code","refresh_token")//允许授权类型
.scopes("emp")//允许授权类型 为emp
.autoApprove(false)//否需要跳转到授权页面让用户确认如果为true回直接发令牌
.redirectUris("http://www.baidu.com");
}
@Bean //建token存储在内存中
public TokenStore tokenStore(){
return new InMemoryTokenStore();
}
//配置令牌
@Autowired
private ClientDetailsService clientDetailsService;
public AuthorizationServerTokenServices tokenService(){
//设置默认的令牌服务
DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
//指定客户端详情配置
defaultTokenServices.setClientDetailsService(clientDetailsService);
//支持刷新token
defaultTokenServices.setSupportRefreshToken(true);
//token存储方式
defaultTokenServices.setTokenStore(tokenStore());
//token有效时间
defaultTokenServices.setAccessTokenValiditySeconds(43200);//秒为单位
//刷新默认有效时间
defaultTokenServices.setRefreshTokenValiditySeconds(14400);
return defaultTokenServices;
}
//配置令牌访问端点
@Autowired
private AuthenticationManager authenticationManager;
@Bean//授权服务的bean
public AuthorizationCodeServices authorizationCodeServices(){
//基于内存存储的授权码服务
return new InMemoryAuthorizationCodeServices();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)//配置认证管理器
.authorizationCodeServices(authorizationCodeServices())//授权服务
.tokenServices(tokenService())//配置令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);//允许post请求
super.configure(endpoints);
}
//授权服务安全配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permiAll()")//放行检查token
.checkTokenAccess("permiAll")//放行token检查
.allowFormAuthenticationForClients();//允许客户端进行表单身份验证,使用表单认证申请令牌
}
}
//资源服务配置
@Configuration
@EnableResourceServer //开启资源服务配置
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig implements ResourceServerConfigurer {
//资源id
private static final String RESOURCE_ID="res1";
//资源服务安全性性配置
@Bean
public ResourceServerTokenServices resourceServerTokenServices(){
//使用远程服务请求授权服务效验token
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
//请求中必须携带token
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:4000/oauth/check_token");
//客户端的id
remoteTokenServices.setClientId("itsource");
//密钥
remoteTokenServices.setClientSecret("wyj");
return remoteTokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resourceServerSecurityConfigurer) throws Exception {
resourceServerSecurityConfigurer.resourceId(RESOURCE_ID)//资源id
.tokenServices(resourceServerTokenServices());//验证令牌
}
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/**").access("#oauth2.hasScope('emp')")//校验scope必须为all , 对应认证服务的客户端详情配置的clientId
.and().csrf().disable()//关闭跨越伪造检查
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//把session设置成无状态,使用了token不在用session做记录
}
}