1.导入依赖
org.springframework.cloud spring-cloud-starter-oauth2org.springframework.security spring-security-jwt1.0.10.RELEASE
2.认证服务配置
package com.itheima.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.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.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService(){
//在内存中提供自定义的用户名 密码
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//在内存中自定义用户名称:jack 密码:jack 权限:p1
manager.createUser(User.withUsername("third").password(passwordEncoder().encode("third")).authorities("user:query").build());
manager.createUser(User.withUsername("jack").password(passwordEncoder().encode("jack")).authorities("p1").build());
manager.createUser(User.withUsername("rose").password(passwordEncoder().encode("rose")).authorities("p2").build());
return manager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//客户端信息暂存储在内存中,也可改为security提供的表存储
//授权码模式下需要的客户端信息如下
clients.inMemory()
.withClient("third_client") //客户端标识
.secret(passwordEncoder().encode("third_secret")) //客户端的秘钥
.authorizedGrantTypes("authorization_code")
.autoApprove(true)
.scopes("all")
//.autoApprove("all")
.redirectUris("http://www.baidu.com") //设置回调地址
//密码模式需要的客户端信息
.and()
.withClient("app_client") //客户端标识
.secret(passwordEncoder().encode("app_secret")) //客户端的秘钥
.authorizedGrantTypes("password", "refresh_token")
.scopes("all") //设置回调地址
.and()
.withClient("pc_client")
.secret(passwordEncoder().encode("pc_secret"))
.authorizedGrantTypes("password", "refresh_token")
.scopes("all");
}
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
//是否支持刷新令牌
service.setSupportRefreshToken(true);
//令牌存储
service.setTokenStore(tokenStore);
// 令牌有效期单位秒,默认默认12小时 设置为1周
service.setAccessTokenValiditySeconds(604800);
// 刷新令牌默认单位秒 默认30天 设置为3周
service.setRefreshTokenValiditySeconds(1814400);
return service;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
.tokenServices(tokenService());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.allowFormAuthenticationForClients(); //支持密码模式下表单登录
}
}
3.token加密方式配置
package com.happyu.auth.config;
import jdk.nashorn.internal.parser.Token;
import org.junit.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
@Configuration
public class TokenConfig {
//对称加密采用秘钥
private static final String secret = "itcast_auth";
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "itcast".toCharArray()).getKeyPair("oauth2");
jwtAccessTokenConverter.setKeyPair(keyPair);
// jwtAccessTokenConverter.setSigningKey(secret);
return jwtAccessTokenConverter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
}
4.密码模式下需要:用户认证时需要的认证管理和用户信息来源
package com.happyu.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
5.配置认证器
package com.happyu.auth.integration.authenticator;
import com.happyu.sys.dto.CompanyUserDTO;
import javax.servlet.http.HttpServletRequest;
public interface LoginHandler {
CompanyUserDTO queryUser(HttpServletRequest request);
boolean support(HttpServletRequest request);
}
6.自定义UserDetailService
package com.happyu.auth.integration.service;
import cn.hutool.core.collection.CollectionUtil;
import com.happyu.auth.integration.authenticator.LoginHandler;
import com.happyu.common.threadlocals.UserHolder;
import com.happyu.common.util.BeanHelper;
import com.happyu.common.util.JsonUtils;
import com.happyu.common.vo.UserInfo;
import com.happyu.sys.dto.CompanyUserDTO;
import com.happyu.sys.entity.Function;
import com.happyu.sys.entity.Role;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private List loginHandlers;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("调用到自定义详情服务MyUserDetailsService--查询用户信息");
//1.获取用户提交认证方式
//1.1 如果在普通类中获取请求参数 采用RequestContextHolder请求上下文对象
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//1.2 转为ServletRequestAttributes
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
//1.3 获取请求对象 进一步 获取请求参数
HttpServletRequest request = servletRequestAttributes.getRequest();
log.info("认证参数:"+request.getParameter("username") +", "+request.getParameter("auth_type"));
//2.根据客户端认证方式,选择一个认证器进行 获取数据库中 员工信息
//选择认证器
LoginHandler loginHandler = chooseLoginHandler(request);
CompanyUserDTO companyUserDTO = loginHandler.queryUser(request);
//3.封装框架要求的返回结果 UserDetails 用户详情服务
//3.1 封装用户权限信息
List authorities = this.getAuthorities(companyUserDTO);
if (CollectionUtils.isEmpty(authorities)) {
//如果用户没有权限 增加 游客 角色
authorities.add(new SimpleGrantedAuthority("ROLE_USER_TOURIST"));
}
//3.2 User对象参数一:生成jwt令牌中 user_name 属性值 问题:将来资源服务器无法得到用户ID标识
//3.3 解决 将CompanyUserDTO转为 自定义对象:UserInfo
UserInfo userInfo = BeanHelper.copyProperties(companyUserDTO, UserInfo.class);
//将用户信息存入userholder
UserHolder.setUser(userInfo);
return new User(JsonUtils.toJsonStr(userInfo), companyUserDTO.getPassword(), authorities);
//x.后续框架还会继续进行校验用户信息,客户端信息是否合法 ,最终才会根据用户信息产生令牌
}
private List getAuthorities(CompanyUserDTO companyUserDTO) {
//1.封装角色权限
List authorities = new ArrayList<>();
List roleList = companyUserDTO.getSysRoles();
if (CollectionUtil.isNotEmpty(roleList)) {
for (Role role : roleList) {
GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
authorities.add(simpleGrantedAuthority);
}
}
//2.封装权限
List functionList = companyUserDTO.getSysFunctions();
if (CollectionUtil.isNotEmpty(functionList)) {
for (Function function : functionList) {
GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(function.getName());
authorities.add(simpleGrantedAuthority);
}
}
return authorities;
}
private LoginHandler chooseLoginHandler(HttpServletRequest request) {
//1.遍历认证器集合
for (LoginHandler loginHandler : loginHandlers) {
//2.每个认证器都有support方法 只要support返回true 直接返回当前实例对象
boolean support = loginHandler.support(request);
//2.1 进入某个方法实现
if (support) {
return loginHandler;
}
}
throw new OAuth2Exception("不支持当前认证方式");
}
}
6.在需要使用认证的微服务上添加资源配置和token配置



