自己搭建比较复杂:直接给个现成的认证服务。参数自己修改为自己的就行
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xc-service-ucenter-auth</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: ${PORT:40400}
servlet:
context-path: /auth
spring:
application:
name: xc-service-ucenter-auth
redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
timeout: 5000 #连接超时 毫秒
jedis:
pool:
maxActive: 3
maxIdle: 3
minIdle: 1
maxWait: -1 #连接池最大等行时间 -1没有限制
datasource:
druid:
url: ${MYSQL_URL:jdbc:mysql://localhost:3306/xc_user?characterEncoding=utf-8}
username: root
password: 123
driverClassName: com.mysql.jdbc.Driver
initialSize: 5 #初始建立连接数量
minIdle: 5 #最小连接数量
maxActive: 20 #最大连接数量
maxWait: 10000 #获取连接最大等待时间,毫秒
testOnBorrow: true #申请连接时检测连接是否有效
testOnReturn: false #归还连接时检测连接是否有效
timeBetweenEvictionRunsMillis: 60000 #配置间隔检测连接是否有效的时间(单位是毫秒)
minEvictableIdleTimeMillis: 300000 #连接在连接池的最小生存时间(毫秒)
auth:
tokenValiditySeconds: 1200 #token存储到redis的过期时间
clientId: XcWebApp #定义header头信息的参数id
clientSecret: XcWebApp #定义header头信息的参数内容
cookieDomain: xuecheng.com #指定一个域名携带cookies
cookieMaxAge: -1 #cookies失效时间,毫秒值
encrypt:
key-store:
location: classpath:/xc.keystore #密钥库文件
secret: xuechengkeystore #密钥库的密码
alias: xckey #密钥别名
password: xuecheng #密钥库的密码
eureka:
client:
registerWithEureka: true #服务注册开关
fetchRegistry: true #服务发现开关
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔
defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ip-address: ${IP_ADDRESS:127.0.0.1}
instance-id: ${spring.application.name}:${server.port} #指定实例id
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间
UcenterAuthApplication 启动类
//开启eruk发现
@EnableDiscoveryClient
//允许远程调用
@EnableFeignClients
@EntityScan("com.xuecheng.framework.domain.ucenter")//扫描实体类
@ComponentScan(basePackages={"com.xuecheng.api"})//扫描接口
@ComponentScan(basePackages={"com.xuecheng.framework"})//扫描common下的所有类
@SpringBootApplication
public class UcenterAuthApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterAuthApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
config 配置包
AuthorizationServerConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
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 javax.annotation.Resource;
import javax.sql.DataSource;
import java.security.KeyPair;
@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
//jwt令牌转换器
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
UserDetailsService userDetailsService;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
TokenStore tokenStore;
@Autowired
private CustomUserAuthenticationConverter customUserAuthenticationConverter;
//读取密钥的配置
@Bean("keyProp")
public KeyProperties keyProperties(){
return new KeyProperties();
}
@Resource(name = "keyProp")
private KeyProperties keyProperties;
//客户端配置
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(this.dataSource).clients(this.clientDetails());
/* clients.inMemory()
.withClient("XcWebApp")//客户端id
.secret("XcWebApp")//密码,要保密
.accessTokenValiditySeconds(60)//访问令牌有效期
.refreshTokenValiditySeconds(60)//刷新令牌有效期
//授权客户端请求认证服务的类型authorization_code:根据授权码生成令牌,
// client_credentials:客户端认证,refresh_token:刷新令牌,password:密码方式认证
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password")
.scopes("app");//客户端范围,名称自定义,必填*/
}
//token的存储方法
// @Bean
// public InMemoryTokenStore tokenStore() {
// //将令牌存储到内存
// return new InMemoryTokenStore();
// }
// @Bean
// public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory){
// RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
// return redisTokenStore;
// }
@Bean
@Autowired
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory
(keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getSecret().toCharArray())
.getKeyPair(keyProperties.getKeyStore().getAlias(),keyProperties.getKeyStore().getPassword().toCharArray());
converter.setKeyPair(keyPair);
//配置自定义的CustomUserAuthenticationConverter
DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
return converter;
}
//授权服务器端点配置
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
/*Collection tokenEnhancers = applicationContext.getBeansOfType(TokenEnhancer.class).values();
TokenEnhancerChain tokenEnhancerChain=new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(new ArrayList<>(tokenEnhancers));
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setReuseRefreshToken(true);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(tokenStore);
defaultTokenServices.setAccessTokenValiditySeconds(1111111);
defaultTokenServices.setRefreshTokenValiditySeconds(1111111);
defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
//.tokenStore(tokenStore);
.tokenServices(defaultTokenServices);*/
endpoints.accessTokenConverter(jwtAccessTokenConverter)
.authenticationManager(authenticationManager)//认证管理器
.tokenStore(tokenStore)//令牌存储
.userDetailsService(userDetailsService);//用户信息service
}
//授权服务器的安全配置
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// oauthServer.checkTokenAccess("isAuthenticated()");//校验token需要认证通过,可采用http basic认证
oauthServer.allowFormAuthenticationForClients()
.passwordEncoder(new BCryptPasswordEncoder())
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}
CustomUserAuthenticationConverter
import com.xuecheng.auth.service.UserJwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
@Component
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
@Autowired
UserDetailsService userDetailsService;
@Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
LinkedHashMap response = new LinkedHashMap();
String name = authentication.getName();
response.put("user_name", 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("utype",userJwt.getUtype());
response.put("userpic",userJwt.getUserpic());
response.put("companyId",userJwt.getCompanyId());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
}
WebSecurityConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@Order(-1)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
//以下路径不被,登录令牌拦截
web.ignoring().antMatchers("/userlogin","/userlogout","/userjwt");
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
//采用bcrypt对密码进行编码
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic().and()
.formLogin()
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
controller 包:
AuthController
import com.xuecheng.api.auth.AuthControllerApi;
import com.xuecheng.auth.service.AuthService;
import com.xuecheng.framework.domain.ucenter.ext.AuthToken;
import com.xuecheng.framework.domain.ucenter.request.LoginRequest;
import com.xuecheng.framework.domain.ucenter.response.AuthCode;
import com.xuecheng.framework.domain.ucenter.response.LoginResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.framework.utils.CookieUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletResponse;
/**
* @author Administrator
* @version 1.0
**/
@RestController
@RequestMapping("/")
public class AuthController implements AuthControllerApi {
@Value("${auth.clientId}")
String clientId;
@Value("${auth.clientSecret}")
String clientSecret;
@Value("${auth.cookieDomain}")
String cookieDomain;
@Value("${auth.cookieMaxAge}")
int cookieMaxAge;
@Autowired
AuthService authService;
@Override
@PostMapping("/userlogin")
public LoginResult login(LoginRequest loginRequest) {
if(loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername())){
ExceptionCast.cast(AuthCode.AUTH_USERNAME_NONE);
}
if(loginRequest == null || StringUtils.isEmpty(loginRequest.getPassword())){
ExceptionCast.cast(AuthCode.AUTH_PASSWORD_NONE);
}
//账号
String username = loginRequest.getUsername();
//密码
String password = loginRequest.getPassword();
//申请令牌
AuthToken authToken = authService.login(username,password,clientId,clientSecret);
//用户身份令牌
String access_token = authToken.getAccess_token();
//将令牌存储到cookie
this.saveCookie(access_token);
return new LoginResult(CommonCode.SUCCESS,access_token);
}
//将令牌存储到cookie
private void saveCookie(String token){
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
//HttpServletResponse response,String domain,String path, String name, String value, int maxAge,boolean httpOnly
CookieUtil.addCookie(response,cookieDomain,"/","uid",token,cookieMaxAge,false);
}
@Override
public ResponseResult logout() {
return null;
}
}
import lombok.Data;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
@Data
@ToString
public class UserJwt extends User {
private String id;
private String name;
private String userpic;
private String utype;
private String companyId;
public UserJwt(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
}
UserDetailsServiceImpl
import com.xuecheng.framework.domain.ucenter.XcMenu;
import com.xuecheng.framework.domain.ucenter.ext.XcUserExt;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
ClientDetailsService clientDetailsService;
@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,clientSecret,AuthorityUtils.commaSeparatedStringToAuthorityList(""));
}
}
if (StringUtils.isEmpty(username)) {
return null;
}
XcUserExt userext = new XcUserExt();
userext.setUsername("itcast");
userext.setPassword(new BCryptPasswordEncoder().encode("123"));
userext.setPermissions(new ArrayList<XcMenu>());
if(userext == null){
return null;
}
//取出正确密码(hash值)
String password = userext.getPassword();
//这里暂时使用静态密码
// String password ="123";
//用户权限,这里暂时使用静态数据,最终会从数据库读取
//从数据库获取权限
List<XcMenu> permissions = userext.getPermissions();
List<String> user_permission = new ArrayList<>();
permissions.forEach(item-> user_permission.add(item.getCode()));
// user_permission.add("course_get_baseinfo");
// user_permission.add("course_find_pic");
String user_permission_string = StringUtils.join(user_permission.toArray(), ",");
UserJwt userDetails = new UserJwt(username,
password,
AuthorityUtils.commaSeparatedStringToAuthorityList(user_permission_string));
userDetails.setId(userext.getId());
userDetails.setUtype(userext.getUtype());//用户类型
userDetails.setCompanyId(userext.getCompanyId());//所属企业
userDetails.setName(userext.getName());//用户名称
userDetails.setUserpic(userext.getUserpic());//用户头像
/* UserDetails userDetails = new org.springframework.security.core.userdetails.User(username,
password,
AuthorityUtils.commaSeparatedStringToAuthorityList(""));*/
// AuthorityUtils.createAuthorityList("course_get_baseinfo","course_get_list"));
return userDetails;
}
}
AuthService
import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.client.XcServiceList;
import com.xuecheng.framework.domain.ucenter.ext.AuthToken;
import com.xuecheng.framework.domain.ucenter.response.AuthCode;
import com.xuecheng.framework.exception.ExceptionCast;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class AuthService {
@Value("${auth.tokenValiditySeconds}")
int tokenValiditySeconds;
@Autowired
LoadBalancerClient loadBalancerClient;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RestTemplate restTemplate;
//用户认证申请令牌,将令牌存储到redis
public AuthToken login(String username, String password, String clientId, String clientSecret) {
//请求spring security申请令牌
AuthToken authToken = this.applyToken(username, password, clientId, clientSecret);
if(authToken == null){
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
//用户身份令牌
String access_token = authToken.getAccess_token();
//存储到redis中的内容
String jsonString = JSON.toJSONString(authToken);
//将令牌存储到redis
boolean result = this.saveToken(access_token, jsonString, tokenValiditySeconds);
if (!result) {
ExceptionCast.cast(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
}
return authToken;
}
//存储到令牌到redis
/**
*
* @param access_token 用户身份令牌
* @param content 内容就是AuthToken对象的内容
* @param ttl 过期时间
* @return
*/
private boolean saveToken(String access_token,String content,long ttl){
// 用户身份令牌做key
String key = "user_token:" + access_token;
// 存入redis,令牌内容,过期时间,设置为秒
stringRedisTemplate.boundValueOps(key).set(content,ttl, TimeUnit.SECONDS);
//存入完成,再查询一次,只要有这条数据,返回值就大于0
Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
return expire>0;
}
//申请令牌
private AuthToken applyToken(String username, String password, String clientId, String clientSecret){
//从eureka中获取认证服务的地址(因为spring security在认证服务中)
//从eureka中获取认证服务的一个实例的地址
ServiceInstance serviceInstance = loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
//此地址就是http://ip:port
URI uri = serviceInstance.getUri();
//令牌申请的地址 http://localhost:40400/auth/oauth/token
String authUrl = uri+ "/auth/oauth/token";
//定义header
LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
String httpBasic = getHttpBasic(clientId, clientSecret);
header.add("Authorization",httpBasic);
//定义body,用密码模式授权,账号,密码,输进去
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type","password");
body.add("username",username);
body.add("password",password);
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
//String url, HttpMethod method, @Nullable HttpEntity> requestEntity, Class responseType, Object... uriVariables
//设置restTemplate远程调用时候,对400和401不让报错,正确返回数据
restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
super.handleError(response);
}
}
});
ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
//申请令牌信息
Map bodyMap = exchange.getBody();
if(bodyMap == null ||
// redis令牌,刷新令牌,短令牌,都不能为空
bodyMap.get("access_token") == null ||
bodyMap.get("refresh_token") == null ||
bodyMap.get("jti") == null){
return null;
}
AuthToken authToken = new AuthToken();
authToken.setAccess_token((String) bodyMap.get("jti"));//用户身份令牌
authToken.setRefresh_token((String) bodyMap.get("refresh_token"));//刷新令牌
authToken.setJwt_token((String) bodyMap.get("access_token"));//jwt令牌
return authToken;
}
//获取httpbasic的串
private String getHttpBasic(String clientId,String clientSecret){
String string = clientId+":"+clientSecret;
//将串进行base64编码
byte[] encode = Base64Utils.encode(string.getBytes());
return "Basic "+new String(encode);
}
}
import com.xuecheng.framework.client.XcServiceList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
/**
* @author Administrator
* @version 1.0
**/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestClient {
@Autowired
LoadBalancerClient loadBalancerClient;
@Autowired
RestTemplate restTemplate;
//远程请求spring security获取令牌
@Test
public void testClient(){
//从eureka中获取认证服务的地址(因为spring security在认证服务中)
//从eureka中获取认证服务的一个实例的地址
ServiceInstance serviceInstance = loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
//此地址就是http://ip:port
URI uri = serviceInstance.getUri();
//令牌申请的地址 http://localhost:40400/auth/oauth/token
String authUrl = uri+ "/auth/oauth/token";
//定义header
LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
String httpBasic = getHttpBasic("XcWebApp", "XcWebApp");
header.add("Authorization",httpBasic);
//定义body
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type","password");
body.add("username","itcast");
body.add("password","12322");
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
//String url, HttpMethod method, @Nullable HttpEntity> requestEntity, Class responseType, Object... uriVariables
//设置restTemplate远程调用时候,对400和401不让报错,正确返回数据
restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
super.handleError(response);
}
}
});
ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
//申请令牌信息
Map bodyMap = exchange.getBody();
System.out.println(bodyMap);
}
//获取httpbasic的串
private String getHttpBasic(String clientId,String clientSecret){
String string = clientId+":"+clientSecret;
//将串进行base64编码
byte[] encode = Base64Utils.encode(string.getBytes());
return "Basic "+new String(encode);
}
}
TestJwt
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.test.context.junit4.SpringRunner;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
/**
* @author Administrator
* @version 1.0
**/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestJwt {
//创建jwt令牌
@Test
public void testCreateJwt(){
//密钥库文件
String keystore = "xc.keystore";
//密钥库的密码
String keystore_password = "xuechengkeystore";
//密钥库文件路径
ClassPathResource classPathResource = new ClassPathResource(keystore);
//密钥别名
String alias = "xckey";
//密钥的访问密码
String key_password = "xuecheng";
//密钥工厂
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,keystore_password.toCharArray());
//密钥对(公钥和私钥)
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, key_password.toCharArray());
//获取私钥
RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
//jwt令牌的内容
Map<String,String> body = new HashMap<>();
body.put("name","itcast");
String bodyString = JSON.toJSONString(body);
//生成jwt令牌
Jwt jwt = JwtHelper.encode(bodyString, new RsaSigner(aPrivate));
//生成jwt令牌编码
String encoded = jwt.getEncoded();
System.out.println(encoded);
}
//校验jwt令牌
@Test
public void testVerify(){
//公钥
String publickey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnASXh9oSvLRLxk901HANYM6KcYMzX8vFPnH/To2R+SrUVw1O9rEX6m1+rIaMzrEKPm12qPjVq3HMXDbRdUaJEXsB7NgGrAhepYAdJnYMizdltLdGsbfyjITUCOvzZ/QgM1M4INPMD+Ce859xse06jnOkCUzinZmasxrmgNV3Db1GtpyHIiGVUY0lSO1Frr9m5dpemylaT0BV3UwTQWVW9ljm6yR3dBncOdDENumT5tGbaDVyClV0FEB1XdSKd7VjiDCDbUAUbDTG1fm3K9sx7kO1uMGElbXLgMfboJ963HEJcU01km7BmFntqI5liyKheX+HBUCD4zbYNPw236U+7QIDAQAB-----END PUBLIC KEY-----";
//jwt令牌
String jwtString = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaXRjYXN0In0.lQOqL1s4DpDHROUAibkz6EMf6hcM7HmTPgmg-SlkacVoQAV7y3XQ7LXxiua6SJlN_uNX_EFjzIshEg_kyy972DtymtRMc2NIO5HzIF5I4oQCxNPsJdhu6qQni6sTas3q0JbAarMZSajDX7HhzVSYWPQJCussA4e1r9oFxDcoAo6TEAXOW8gRHzNIygQz1yCj6mdf4UOHI070kRy7f3BdhmrUJdOuDIMoRBYS4WsEOibAU1UCNPaJAXpZC0ihrtdY7SCg1N43fimeFOHrfpLb6OmRF7v7uvGMgrhg9JIYDbJ6nbode5OJkNceRx8QUICre2yKAe0ctlvXO0REf6OpRA";
//校验jwt令牌
Jwt jwt = JwtHelper.decodeAndVerify(jwtString, new RsaVerifier(publickey));
//拿到jwt令牌中自定义的内容
String claims = jwt.getClaims();
System.out.println(claims);
}
}
TestRedis
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author Administrator
* @version 1.0
**/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestRedis {
@Autowired
StringRedisTemplate stringRedisTemplate;
//创建jwt令牌
@Test
public void testRedis(){
//定义key
String key = "user_token:abde2951-76ab-4a24-8f5e-08604bb46f97";
//定义value
Map<String,String> value = new HashMap<>();
value.put("jwt","eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55SWQiOm51bGwsInVzZXJwaWMiOm51bGwsInVzZXJfbmFtZSI6Iml0Y2FzdCIsInNjb3BlIjpbImFwcCJdLCJuYW1lIjpudWxsLCJ1dHlwZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTUzODE1Mzc5NSwianRpIjoiYWJkZTI5NTEtNzZhYi00YTI0LThmNWUtMDg2MDRiYjQ2Zjk3IiwiY2xpZW50X2lkIjoiWGNXZWJBcHAifQ.SaJyy_-wMFcXhMyYK2D8dZ38vXZz1usbA58fraqnCv9fFvWrbPJ_5aG2QxY7yGOUKJY93SPntE3Y9K3qy2MdGGFMGWFHMcpgiNfOHLJNliOr-EnY2CP-EPZv9iaz2gRX2--kn3rqM9LWwev_ELWcnvwR3g2HDbvQjZ7TudiTMoOaMfdkUbGTYOwB2-Pa9FD46DTN3V5AtyJEyi72rIrPM2xY2JDjSTcFEb6KWdyTx8ELBNCkY0U7p6ioNxApGjmmV5Z1b1iMcNM1ipuSlqr0sjg88dAb38QX5JUzWB9Sn-qobHg1pQjj4A3mDt-Vs1jqB3YBM0KAlylzEz50vaCXIw");
value.put("refresh_token","eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21wYW55SWQiOm51bGwsInVzZXJwaWMiOm51bGwsInVzZXJfbmFtZSI6Iml0Y2FzdCIsInNjb3BlIjpbImFwcCJdLCJhdGkiOiJhYmRlMjk1MS03NmFiLTRhMjQtOGY1ZS0wODYwNGJiNDZmOTciLCJuYW1lIjpudWxsLCJ1dHlwZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTUzODE1Mzc5NSwianRpIjoiZTcyYWExODYtZjBiMC00NDU0LTk3N2EtYWVmODQ4MDRhZjJiIiwiY2xpZW50X2lkIjoiWGNXZWJBcHAifQ.ZIEvVhiautHYREhVXDfRYVcSfjbxGeu-YXvqZyqe3QEZN9iNcmnn6QDbCy9IHzjyw5mOhUHB3VhZHc3wOv6lkOOiSUINTDsr4ZR-PeuIU2-GSU-aAqhX3iamG2lKWFWLkXeqKJqJc9K25oAmtRpisdO6_uJuJq0vygxAbS_tE-dDYedUYYxD86mFRQPCTmDlqAlxN40TLQBACQeHsHuGtzkaOdfCLMKI0c_4Or1uLOUkhofpPHPE4_ZTJ5QhrkJui-5s51gKWqcReyVh3X9thorU5KuMwFd0bwT3jspsQV5UyO9FVxqmns5iI9zn-xya16FIO_RR-1uIi9ruKJ3llA");
String jsonString = JSON.toJSONString(value);
//校验key是否存在,如果不存在则返回-2
Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
System.out.println(expire);
//存储数据
stringRedisTemplate.boundValueOps(key).set(jsonString,30, TimeUnit.SECONDS);
//获取数据
String string = stringRedisTemplate.opsForValue().get(key);
System.out.println(string);
}
}
配置nginx参数:
#认证
location ^~ /openapi/auth/ {
proxy_pass http://auth_server_pool/auth/;
}
#认证服务
upstream auth_server_pool{
server 127.0.0.1:40400 weight=10;
}