1. gateway-service
pom
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!-- 配置-->
starter-config
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
starter-netflix-eureka-client
</dependencies>
yaml 和 enable
spring:
application:
name: gateway-service
cloud:
config:
uri: http://localhost:8769
fail-fast: true
profiles:
active: pro
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
2. admin-service
pom
<dependencies>
starter-config
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.1.0</version>
</dependency>
starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
eureka-client
<dependency> mail
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
</dependencies>
yaml 和 logback-spring 和 enable
spring:
application:
name: admin-service
cloud:
config:
uri: http://localhost:8769
fail-fast: true
profiles:
active: pro
G版本 已经不需要配置 turbine了吧
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator/>
</configuration>
@SpringBootApplication
@EnableDiscoveryClient
@EnableAdminServer
代码
security 配置
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter( "redirectTo" );
http.authorizeRequests() //这两个放心,其他都需要权限
.antMatchers( adminContextPath + "/assets/**" ).permitAll()
.antMatchers( adminContextPath + "/login" ).permitAll()
.anyRequest().authenticated()
.and() //登录,登录成功的处理
.formLogin().loginPage( adminContextPath + "/login" ).successHandler( successHandler ).and() //登出,登出的处理。 基本拦截 csrf
.logout().logoutUrl( adminContextPath + "/logout" ).and()
.httpBasic().and()
.csrf().disable();
// @formatter:on
}
}
Notifier config
//import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
//import de.codecentric.boot.admin.server.notify.CompositeNotifier;
//import de.codecentric.boot.admin.server.notify.Notifier;
//import de.codecentric.boot.admin.server.notify.RemindingNotifier;
//import de.codecentric.boot.admin.server.notify.filter.FilteringNotifier;
//import org.springframework.beans.factory.ObjectProvider;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//
//import java.time.Duration;
//import java.util.Collections;
//import java.util.List;
//
//@Configuration
//public class NotifierConfig {
//
// private final InstanceRepository repository;
// private final ObjectProvider<List<Notifier>> otherNotifiers;
//
// public NotifierConfig(InstanceRepository repository, ObjectProvider<List<Notifier>> otherNotifiers) {
// this.repository = repository;
// this.otherNotifiers = otherNotifiers;
// }
//
// @Bean
// public FilteringNotifier filteringNotifier() {
// CompositeNotifier delegate = new CompositeNotifier(otherNotifiers.getIfAvailable(Collections::emptyList));
// return new FilteringNotifier(delegate, repository);
// }
//
// @Primary
// @Bean(initMethod = "start", destroyMethod = "stop")
// public RemindingNotifier remindingNotifier() {
// RemindingNotifier notifier = new RemindingNotifier(filteringNotifier(), repository);
// notifier.setReminderPeriod(Duration.ofMinutes(10));
// notifier.setCheckReminderInverval(Duration.ofSeconds(10));
// return notifier;
// }
//}
3. user-service
pom
<dependencies>
<dependency>
<groupId>com.forezp</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
starter-netflix-eureka-client
<!-- 配置-->
starter-config
<!-- 开启web-->
starter-web
<!-- 开启feign-->
starter-openfeign
<!-- dashboard -->
<!-- actuator-->
starter-actuator
<!--hystrix-dashboard-->
starter-netflix-hystrix-dashboard
<!--hystrix -->
starter-netflix-hystrix
<!-- zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!--database-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- mq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
yaml 和 main
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8769
fail-fast: true
profiles:
active: pro
# datasource:
# driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
# username: root
# password: 123456
# jpa:
# hibernate:
# ddl-auto: create
# show-sql: true
#
# rabbitmq:
# host: localhost
# port: 5672
# username: guest
# password: guest
# publisher-confirms: true
# virtual-host: /
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //feign
@EnableHystrixDashboard//dashboard
@EnableHystrix//hystrix
public class UserServiceApplication {
}
- public.cert
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjuyo7NI2wdwKkvHxHHlo
J374F2mUKhxVzIFjnf9TJWKC5OqQm5IbcnsiLHp6w3ElGOgyXBJhiqZPJ6GhTq16
4/XsxSMVRlnmWuggSdDpVIGoYoeGuLFhLXI10IHYvN0pIAynR741HCwp1lYHOabq
C4S/lvi9jQSbU+mAz3eq9iGLwAPV/WDkbcQL1PX4yjQ4JN81qzu81zJXlN8KhgR4
oRbyl3FPLYOaxwIRddFKgkBeS0qiQMDaXDmDf8AU5EUEeUwZoWoFarkcPg6jbQ87
Sr0vMLYApKBN6GJObNzZfDmNJ0GbBZvYc0zUib+A5vPStBEtszGuyb2K9jtlCyJi
qQIDAQAB
-----END PUBLIC KEY-----
aop包:保存日志
@Aspect // 进行切割
@Component //给spring管理
public class SysLoggerAspect {
@Autowired
private LoggerService loggerService;
//切的位置
@Pointcut("@annotation(com.forezp.annotation.SysLogger)")
public void loggerPointCut() {
}
//之前进行操作
@Before("loggerPointCut()")
public void saveSysLog(JoinPoint joinPoint) {
//得到 反射的自然
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//得到 放射的方法
Method method = signature.getMethod();
//创建log
SysLog sysLog = new SysLog();
//从注解上,得到 SysLogger
SysLogger sysLogger = method.getAnnotation(SysLogger.class);
if(sysLogger != null){
//得到:注解上的描述
sysLog.setOperation(sysLogger.value());
}
//请求的方法名 类的路径
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
//拼接成 完整的方法
sysLog.setMethod(className + "." + methodName + "()");
//请求的参数
Object[] args = joinPoint.getArgs();
String params="";
for(Object o:args){
//对象转成 json,拼接
params+=JSON.toJSONString(o);
}
//如果 参数不为空
if(!StringUtils.isEmpty(params)) {
//设置上参数
sysLog.setParams(params);
}
//设置IP地址
sysLog.setIp(HttpUtils.getIpAddress());
//用户名
String username = UserUtils.getCurrentPrinciple();
if(!StringUtils.isEmpty(username)) {
//设置上用户名
sysLog.setUsername(username);
}
//日志的创建时间
sysLog.setCreateDate(new Date());
//保存系统日志
loggerService.log(sysLog);
}
}
feign 到 Uaa
@FeignClient(value = "uaa-service",fallback =AuthServiceHystrix.class )
public interface AuthServiceClient {
@PostMapping(value = "/oauth/token") //注意路路径 /oauth/token 。第一参数是 header
JWT getToken(@RequestHeader(value = "Authorization") String authorization,
@RequestParam("grant_type") String type,
@RequestParam("username") String username,
@RequestParam("password") String password);
}
@Component
public class AuthServiceHystrix implements AuthServiceClient {
@Override
public JWT getToken(String authorization, String type, String username, String password) {
System.out.println("--------opps getToken hystrix---------");
return null;
}
}
config
security
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfiguration {
}
jwt config
@Configuration
public class JwtConfiguration {
@Autowired //jwt token converter
JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
@Qualifier("tokenStore")
public TokenStore tokenStore() {
// 使用 jwt token存储
System.out.println("Created JwtTokenStore");
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
//配置 converter
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//得到result
Resource resource = new ClassPathResource("public.cert");
String publicKey ;
try {
//公钥 = 资源 得到 输入流 copy到数组
publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
//公钥赋值
converter.setVerifierKey(publicKey);
return converter;
}
}
Rabbit Mq 配置
@Configuration
public class RabbitConfig {
public final static String queueName = "spring-boot";
//创建队列
@Bean
Queue queue() {
return new Queue(queueName, false);
}
//创建交换器
@Bean
TopicExchange exchange() {
return new TopicExchange("spring-boot-exchange");
}
//把队列绑定到交换器
@Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
}
ResourceServerConfigurerAdapter
@Configuration
@EnableResourceServer //开启资源 server
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{
Logger log = LoggerFactory.getLogger(ResourceServerConfiguration.class);
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() //禁用csrf
.authorizeRequests()
.regexMatchers(".*swagger.*",".*v2.*",".*webjars.*","/user/login.*","/user/registry.*","/user/test.*",".*actuator.*").permitAll()//这些放开权限,其他都 要授权
.antMatchers("/**").authenticated();
// .antMatchers("/**").permitAll();
}
@Override //资源ID为 user-service ,token 为 tokenStore
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
log.info("Configuring ResourceServerSecurityConfigurer ");
resources.resourceId("user-service").tokenStore(tokenStore);
}
@Autowired
TokenStore tokenStore;
}
swagger2 配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* 全局参数
*
* @return
*/
private List<Parameter> parameter() {
//创建list
List<Parameter> params = new ArrayList<>();
//增加 arameterBuilder ,name desc modelRef 参数类型 是否必须,进行构建
params.add(new ParameterBuilder().name("Authorization")
.description("Authorization Bearer token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false).build());
return params;
}
@Bean //创建 Docket
public Docket sysApi() {
//new Docket ,swagger2 ,apiInfo select apis 路径 构建 全局的参数
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.forezp.web"))
.paths(PathSelectors.any())
.build().globalOperationParameters(parameter());
//.securitySchemes(newArrayList(oauth()))
// .securityContexts(newArrayList(securityContext()));
}
//api info ,ApiInfoBuilder 参数有 title desc termsOfServiceceUrl contact version
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(" user-service api ")
.description("user-service 微服务")
.termsOfServiceUrl("")
.contact("forezp")
.version("1.0")
.build();
}
}
dao dto entiry exception service 包
public interface UserDao extends JpaRepository<User, Long> {
User findByUsername(String username);
}
dto
public class LoginDTO {
private User user;
private String token;
}
entity
public class JWT {
private String access_token;
private String token_type;
private String refresh_token;
private int expires_in;
private String scope;
private String jti;
}
public class SysLog {
private Long id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//请求参数
private String params;
//IP地址
private String ip;
//创建时间
private Date createDate;
}
@Entity
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column
private String password;
public User() {
}
}
exception
@ControllerAdvice //当做是 controller
@ResponseBody //json返回
public class CommonExceptionHandler {
//异常处理
@ExceptionHandler(CommonException.class)
public ResponseEntity<RespDTO> handleException(Exception e) {
//返回对象。common项目的
RespDTO resp = new RespDTO();
//转换成 通用异常
CommonException taiChiException = (CommonException) e;
//返回的状态码
resp.code = taiChiException.getCode();
//返回的错误码
resp.error = e.getMessage();
//进行返回,状态为200
return new ResponseEntity(resp, HttpStatus.OK);
}
}
service
@Service
public class LoggerService {
@Autowired
private AmqpTemplate rabbitTemplate;
public void log(SysLog sysLog){
//发送 日志到 mq,类型为 json
rabbitTemplate.convertAndSend(RabbitConfig.queueName, JSON.toJSONString(sysLog));
}
}
@Service
public class UserService {
@Autowired
UserDao userDao;
@Autowired
AuthServiceClient authServiceClient;
//创建用户
public User createUser(User user){
return userDao.save(user);
}
//根据用户名查询
public User getUserInfo(String username){
return userDao.findByUsername(username);
}
//登录用户
public RespDTO login(String username , String password){
//查询到 用户名
User user= userDao.findByUsername(username);
if(null==user){
throw new CommonException(ErrorCode.USER_NOT_FOUND);
}
//如果库里的密码 和 参数的明文密码 不匹配,报错
if(!BPwdEncoderUtils.matches(password,user.getPassword())){
throw new CommonException(ErrorCode.USER_PASSWORD_ERROR);
}
//基础token的,创建成 jwt
JWT jwt = authServiceClient.getToken("Basic dWFhLXNlcnZpY2U6MTIzNDU2", "password", username, password);
// 获得用户菜单
if(null==jwt){
throw new CommonException(ErrorCode.GET_TOKEN_FAIL);
}
//创建参数赋值
LoginDTO loginDTO=new LoginDTO();
loginDTO.setUser(user);
//设置上 token
loginDTO.setToken(jwt.getAccess_token());
return RespDTO.onSuc(loginDTO);
}
}
controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@ApiOperation(value = "注册", notes = "username和password为必选项")
@PostMapping("/registry")
@SysLogger("registry") //注解,保存日志
public User createUser(@RequestBody User user){
//参数判读省略,判读该用户在数据库是否已经存在省略。加密 入库
String entryPassword= BPwdEncoderUtils.BCryptPassword(user.getPassword());
user.setPassword(entryPassword);
return userService.createUser(user);
}
@ApiOperation(value = "登录", notes = "username和password为必选项")
@PostMapping("/login")
@SysLogger("login") //登录
public RespDTO login(@RequestParam String username , @RequestParam String password){
//参数判读省略
return userService.login(username,password);
}
@ApiOperation(value = "根据用户名获取用户", notes = "根据用户名获取用户")
@PostMapping("/{username}")
@PreAuthorize("hasRole('USER')") //必须有 user权限
@SysLogger("getUserInfo")
// @PreAuthorize("hasAnyAuthority('ROLE_USER')")
public RespDTO getUserInfo(@PathVariable("username") String username){
//参数判读省略
User user= userService.getUserInfo(username);
return RespDTO.onSuc(user);
}
// @Autowired
// private AmqpTemplate rabbitTemplate;
// @GetMapping("/test")
// public void test(){
// rabbitTemplate.convertAndSend(RabbitConfig.queueName, "Hello from RabbitMQ!");
// }
}
util包
b crypt password encoder
public class BPwdEncoderUtils {
//创建
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
/**
* 用BCryptPasswordEncoder 加密
* @param password
* @return
*/
public static String BCryptPassword(String password){
return encoder.encode(password);
}
/**
*
* @param rawPassword 原始密码 。明文。String
* @param encodedPassword 加密后的密码。 库里的 密文
* @return
*/
public static boolean matches(CharSequence rawPassword, String encodedPassword){
return encoder.matches(rawPassword,encodedPassword);
}
}
httpUtils
public class HttpUtils {
/**
* 尝试获取当前请求的HttpServletRequest实例
*
* @return HttpServletRequest
*/
public static HttpServletRequest getHttpServletRequest() {
try {
//RequestContextHolder.getRequestAttributes()
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception e) {
return null;
}
}
public static Map<String, String> getHeaders(HttpServletRequest request) {
//创建 map
Map<String, String> map = new LinkedHashMap<>();
//从请求中 获取 headerNames
Enumeration<String> enumeration = request.getHeaderNames();
//如果存在 进行遍历
while (enumeration.hasMoreElements()) {
//获得到 key
String key = enumeration.nextElement();
//request中 通过key获得 到值
String value = request.getHeader(key);
//放入map
map.put(key, value);
}
return map;
}
/**
* 获取请求客户端的真实ip地址
*
* @param request 请求对象
* @return ip地址
*/
public static String getIpAddress(HttpServletRequest request) {
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//获得远程的IP
ip = request.getRemoteAddr();
}
} else if (ip.length() > 15) {
String[] ips = ip.split(",");
for (int index = 0; index < ips.length; index++) {
String strIp = (String) ips[index];
if (!("unknown".equalsIgnoreCase(strIp))) {
ip = strIp;
break;
}
}
}
return ip;
}
/**
* 获取请求客户端的真实ip地址
*
* @param
* @return ip地址
*/
public static String getIpAddress() {
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
return getIpAddress(getHttpServletRequest());
}
}
User Utils
public class UserUtils {
//定义 token 头
private static final String AUTHORIZATION = "authorization";
/**
* 获取当前请求的token
* @return
*/
public static String getCurrentToken() {
//获得 所有 headers头 ,在获得这个 token 头
return HttpUtils.getHeaders(HttpUtils.getHttpServletRequest()).get(AUTHORIZATION);
}
/**
* 获取当前请求的用户Id
* @return
*/
public static String getCurrentPrinciple() {
//security上下文,获得 上下文,获得 认证信息,获得 getPrincipal
return (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
/**
* 判读当前token用户是否为接口所需的参数username
*
* @param username
* @return
*/
public static boolean isMyself(String username) {
//判断 当前登录的用户,是否和 参数的user name 相同
return username.equals(getCurrentPrinciple());
}
/**
* 获取当前请求Authentication
*
* @return
*/
public static Authentication getCurrentAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* 获取当前请求的权限信息
* @return
*/
public static List<SimpleGrantedAuthority> getCurrentAuthorities() {
//获得认证信息,在调用 getAuthorities() 获权限
return (List<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
}
/**
* @param role 是否包含某个权限
* @return
*/
public static boolean hasRole(String role) {
//如果不是 ROLE 开头的,就 拼接
if (!role.startsWith("ROLE_")) {
role = "ROLE_" + role;
}
//默认不包含权限
boolean hasRole = false;
//获得所有的权限
List<SimpleGrantedAuthority> list = getCurrentAuthorities();
//遍历
for (SimpleGrantedAuthority s : list) {
//如果角色 包含这个权限,就返回true
if (role.equals(s.getAuthority())) {
hasRole = true;
break;
}
}
return hasRole;
}
}