代码:
AuthorizationServerConfig
@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//数据源,用于从数据库获取数据进行认证操作,测试可以从内存中获取
@Autowired
private DataSource dataSource;
//jwt令牌转换器
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
//SpringSecurity 用户自定义授权认证类
@Autowired
UserDetailsService userDetailsService;
//授权认证管理器
@Autowired
AuthenticationManager authenticationManager;
//令牌持久化存储接口
@Autowired
TokenStore tokenStore;
@Autowired
private CustomUserAuthenticationConverter customUserAuthenticationConverter;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).clients(clientDetails());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(jwtAccessTokenConverter)
.authenticationManager(authenticationManager)//认证管理器
.tokenStore(tokenStore) //令牌存储
.userDetailsService(userDetailsService); //用户信息service
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients()
.passwordEncoder(new BCryptPasswordEncoder())
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
//读取密钥的配置
@Bean("keyProp")
public KeyProperties keyProperties(){
return new KeyProperties();
}
@Resource(name = "keyProp")
private KeyProperties keyProperties;
//客户端配置
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Bean
@Autowired
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
JwtAccessTokenConverter
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(
keyProperties.getKeyStore().getLocation(), //证书路径 changgou.jks
keyProperties.getKeyStore().getSecret().toCharArray()) //证书秘钥 changgouapp
.getKeyPair(
keyProperties.getKeyStore().getAlias(), //证书别名 changgou
keyProperties.getKeyStore().getPassword().toCharArray()); //证书密码 changgou
converter.setKeyPair(keyPair);
//配置自定义的CustomUserAuthenticationConverter
DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
return converter;
}
}
@Component
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
@Autowired UserDetailsService userDetailsService; @Override public MapconvertUserAuthentication(Authentication authentication) { linkedHashMap response = new linkedHashMap(); String name = authentication.getName(); response.put("username", name); Object principal = authentication.getPrincipal(); UserJwt userJwt = null; if(principal instanceof UserJwt){ userJwt = (UserJwt) principal; }else{ //refresh_token默认不去调用userdetailService获取用户信息,这里我们手动去调用,得到 UserJwt UserDetails userDetails = userDetailsService.loadUserByUsername(name); userJwt = (UserJwt) userDetails; } response.put("name", userJwt.getName()); response.put("id", userJwt.getId()); //公司 response.put("compy", "songsi"); if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) { response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities())); } return response; }
}
UserDetailsServiceImpl
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired ClientDetailsService clientDetailsService;
@Autowired
private UserFeign userFeign;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//取出身份,如果身份为空说明没有认证
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret
if(authentication==null){
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
if(clientDetails!=null){
//秘钥
String clientSecret = clientDetails.getClientSecret();
//静态方式
//return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));
//数据库查找方式
return new User(username,clientSecret, AuthorityUtils.commaSeparatedStringToAuthorityList(""));
}
}
if (StringUtils.isEmpty(username)) {
return null;
}
//根据用户名查询用户信息
//String pwd = new BCryptPasswordEncoder().encode("szitheima");
String pwd = userFeign.findByUsername(username).getData().getPassword();
//创建User对象 授予权限.GOODS_LIST SECKILL_LIST
String permissions = "goods_list,seckill_list";
UserJwt userDetails = new UserJwt(username,pwd,AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
//userDetails.setComy(songsi);
return userDetails;
}
public static void main(String[] args) {
String zhangsan = new BCryptPasswordEncoder().encode("zhangsan");
System.out.println(zhangsan);
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
@Order(-1)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/user/login",
"/user/logout","/oauth/login","/css
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic() //启用Http基本身份验证
.and()
.formLogin() //启用表单身份验证
.loginPage("/oauth/login")
.loginProcessingUrl("/user/login")
.and()
.authorizeRequests() //限制基于Request请求访问
.anyRequest()
.authenticated(); //其他请求都需要经过验证
}
}
AuthController
@RestController
@RequestMapping(value = “/userx”)
public class AuthController {
//客户端ID
@Value("${auth.clientId}")
private String clientId;
//秘钥
@Value("${auth.clientSecret}")
private String clientSecret;
//cookie存储的域名
@Value("${auth.cookieDomain}")
private String cookieDomain;
//cookie生命周期
@Value("${auth.cookieMaxAge}")
private int cookieMaxAge;
@Autowired
AuthService authService;
@PostMapping("/login")
public Result login(String username, String password) {
if(StringUtils.isEmpty(username)){
throw new RuntimeException(“用户名不允许为空”);
}
if(StringUtils.isEmpty(password)){
throw new RuntimeException(“密码不允许为空”);
}
//申请令牌
AuthToken authToken = authService.login(username,password,clientId,clientSecret);
//用户身份令牌 String access_token = authToken.getAccessToken(); //将令牌存储到cookie savecookie(access_token); return new Result(true, StatusCode.OK,"登录成功!");
}
private void savecookie(String token){
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
cookieUtil.addcookie(response,cookieDomain,"/",“Authorization”,token,cookieMaxAge,false);
}
}
LoginRedirectController
@Controller
@RequestMapping("/oauth")
public class LoginRedirectController {
@RequestMapping("/login")
public String login(String From, Model model) {
model.addAttribute("from",From);
return "login";
}
}
AuthServiceImpl
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@Override
public AuthToken login(String username, String password, String clientId, String clientSecret) {
//申请令牌
AuthToken authToken = applyToken(username,password,clientId, clientSecret);
if(authToken == null){
throw new RuntimeException("申请令牌失败");
}
return authToken;
}
private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
//选中认证服务的地址
ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");
if (serviceInstance == null) {
throw new RuntimeException("找不到对应的服务");
}
//获取令牌的url
String path = serviceInstance.getUri().toString() + "/oauth/token";
//定义body
MultiValueMap formData = new linkedMultiValueMap<>();
//授权方式
formData.add("grant_type", "password");
//账号
formData.add("username", username);
//密码
formData.add("password", password);
//定义头
MultiValueMap header = new linkedMultiValueMap<>();
header.add("Authorization", httpbasic(clientId, clientSecret));
//指定 restTemplate当遇到400或401响应时候也不要抛出异常,也要正常返回值
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
//当响应的值为400或401时候也要正常响应,不要抛出异常
if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
super.handleError(response);
}
}
});
Map map = null;
try {
//http请求spring security的申请令牌接口
ResponseEntity
}
LoginServiceImpl
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@Override
public AuthToken login(String username, String password, String clientId, String clientSecret, String grandType) {
//1.定义url (申请令牌的url)
//参数 : 微服务的名称spring.appplication指定的名称
ServiceInstance choose = loadBalancerClient.choose("user-auth");
String url =choose.getUri().toString()+"/oauth/token";
//2.定义头信息 (有client id 和client secr)
MultiValueMap headers = new linkedMultiValueMap<>();
headers.add("Authorization","Basic "+base64.getEncoder().encodeToString(new String(clientId+":"+clientSecret).getBytes()));
//3. 定义请求体 有授权模式 用户的名称 和密码
MultiValueMap formData = new linkedMultiValueMap<>();
formData.add("grant_type",grandType);
formData.add("username",username);
formData.add("password",password);
//4.模拟浏览器 发送POST 请求 携带 头 和请求体 到认证服务器
HttpEntity requestentity = new HttpEntity(formData,headers);
ResponseEntity
}
public interface AuthService {
AuthToken login(String username, String password, String clientId, String clientSecret);
}
public interface LoginService {
AuthToken login(String username, String password, String clientId, String clientSecret, String grandType);
}
AuthToken
public class AuthToken implements Serializable{
//令牌信息
String accessToken;
//刷新token(refresh_token)
String refreshToken;
//jwt短令牌
String jti;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getJti() {
return jti;
}
public void setJti(String jti) {
this.jti = jti;
}
}
cookieUtil
public class cookieUtil {
public static void addcookie(HttpServletResponse response, String domain, String path, String name,
String value, int maxAge, boolean httpOnly) {
cookie cookie = new cookie(name, value);
cookie.setDomain(domain);
cookie.setPath(path);
cookie.setMaxAge(maxAge);
cookie.setHttponly(httpOnly);
response.addcookie(cookie);
}
public static Map readcookie(HttpServletRequest request, String ... cookieNames) {
Map cookieMap = new HashMap();
cookie[] cookies = request.getcookies();
if (cookies != null) {
for (cookie cookie : cookies) {
String cookieName = cookie.getName();
String cookievalue = cookie.getValue();
for(int i=0;i
}
OAuthApplication
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = “com.changgou.auth.dao”)
@EnableFeignClients(basePackages = {“com.changgou.user.feign”})
public class OAuthApplication {
public static void main(String[] args) {
SpringApplication.run(OAuthApplication.class,args);
}
@Bean(name = "restTemplate")
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Pom.xml
changgou-parent
com.changgou
1.0-SNAPSHOT
4.0.0
changgou-user-oauth
OAuth2.0认证环境搭建
com.changgou
changgou-common-db
1.0-SNAPSHOT
org.springframework.security
spring-security-data
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.cloud
spring-cloud-starter-security
org.springframework.boot
spring-boot-starter-actuator
com.changgou
changgou-service-user-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-thymeleaf
application.yml
server:
port: 9001
spring:
application:
name: user-auth
redis:
host: 192.168.211.132
port: 6379
password:
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.211.132:3306/changgou_oauth?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
username: root
password: 123456
main:
allow-bean-definition-overriding: true
thymeleaf:
cache: false
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
auth:
ttl: 3600 #token存储到redis的过期时间
clientId: changgou
clientSecret: changgou
cookieDomain: localhost
cookieMaxAge: -1
encrypt:
key-store:
location: classpath:/changgou.jks
secret: changgou
alias: changgou
password: changgou
脚本:
申请授权码
GET
http://localhost:9001/oauth/authorize?client_id=changgou&response_type=code&scop=app&redirect_uri=http://localhost
根据授权码获取token
POST
http://localhost:9001/oauth/token
Authorization: username password (Basic Auth)
form-data:
grant_type authorization_code
Code kKghj4
redirect_uri http://localhost
刷新令牌:
POST
http://localhost:9001/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwiYXRpIjoiMjNmYTQwOGEtYzkyMi00NDdmLTk3OTktNjY2NjEzNjEwNTM5IiwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYzNDQ4OTA4MCwiYXV0aG9yaXRpZXMiOlsic2Vja2lsbF9saXN0IiwiZ29vZHNfbGlzdCJdLCJqdGkiOiI0ZjU3MWU5ZS05MGMxLTQ0YzMtOTI1ZS05OTRjODQ4MmQ2OWMiLCJjbGllbnRfaWQiOiJjaGFuZ2dvdSIsInVzZXJuYW1lIjoic3ppdGhlaW1hIn0.MdkCkvQidZ2kJ6YC45-JDtr1sMSRmlEAZH5Vj3IkgLxvLFf5mktPqYMMFLS5K5yut53G5gBRCy792ZYbkC0sT-y5tHAnpGrTwLJPG6ZUI8dr5t2UotX4xDv_zlo4DQiUfmG2kBD8hKQeBaa6Y5WdWndfq11sNmdwZzFIBkGsgT-I15IXtx3UEL5y-kMQ30xpBJG_OlGE9y8RCi1H0Xa-kEPNVvkqYWwawW9mBgasH57yeYTEWYhgVCmYmLV3YZdQOu58JqwHKcFeUwUSya77m2Rr157uOZjxTk3tSoBtUAqLivJNU8XmRAxT5UIU4S5Sv7Egzm3uHjL7LWLKuCZ40g
令牌校验:
POST
http://localhost:9001/oauth/check_token?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYzNDQ4OTA4MCwiYXV0aG9yaXRpZXMiOlsic2Vja2lsbF9saXN0IiwiZ29vZHNfbGlzdCJdLCJqdGkiOiIyM2ZhNDA4YS1jOTIyLTQ0N2YtOTc5OS02NjY2MTM2MTA1MzkiLCJjbGllbnRfaWQiOiJjaGFuZ2dvdSIsInVzZXJuYW1lIjoic3ppdGhlaW1hIn0.BlU5X5ZkJWq0X-AT1Qjg3eRkhvBvtgqpkW6twqn1sM0diXxjlxoZsOJspqHDFlbg2nrgv0uGMohEOhd8-1tLTZyttCTCqRaEkIsx-8u-vQmhmk9WqguGTjXKWZZhYAiXhlfg4SntMiAHYGmSyhoXAaH8GKifQb6Kyp-ChSAvAnwKzkQKNoMdXVTNyGlEn70ZCXw8gMwyJT9DH1Tr3o-sjyZ9Cq6uk4xMC7u-rypgJp2DsedCQo9wh5QH4PlD-QzqJq9FWsPFJnLptDYcXg9wIPRzmQnT33QqG9pDMHo2gCXuIa3oZlTylWcwFyt9ERohafZcbGvQQemp5kwEgQnjDQ
http://localhost:9001/oauth/token
Post
Formdata:
grant_type password
Username szitheima
Password szitheima



