首页 文章 精选 留言 我的

精选列表

搜索[整合],共10000篇文章
优秀的个人博客,低调大师

Spring security(四)-spring boot +spring security短信认证+redis整合

关注我,可以获取最新知识、经典面试题以及技术分享 现在主流的登录方式主要有 3 种:账号密码登录、短信验证码登录和第三方授权登录,前面一节Spring security(三)---认证过程已分析了spring security账号密码方式登陆,现在我们来分析一下spring security短信方式认证登陆。 Spring security 短信方式、IP验证等类似模式登录方式验证,可以根据账号密码方式登录步骤仿写出来,其主要以以下步骤进行展开: 自定义Filter: 自定义Authentication 自定义AuthenticationProvider 自定义UserDetailsService SecurityConfig配置 1. 自定义filter: 自定义filter可以根据UsernamePasswordAuthenticationFilter过滤器进行仿写,其实质即实现AbstractAuthenticationProcessingFilter抽象类,主要流程分为: 构建构造器,并在构造器中进行配置请求路径以及请求方式的过滤 自定义attemptAuthentication()认证步骤 在2步骤中认证过程中需要AuthenticationProvider进行最终的认证,在认证filter都需要将AuthenticationProvider设置进filter中,而管理AuthenticationProvider的是AuthenticationManager,因此我们创建过滤器filter的时候需要设置AuthenticationManager,这步具体详情在5.1 SecurityConfig配置步骤。 在第2步中attemptAuthentication()认证方法主要进行以下步骤: 1).post请求认证; 2).request请求获取手机号码和验证码; 3).用自定义的Authentication对象封装手机号码和验证码; 4).使用AuthenticationManager.authenticate()方法进行验证。 自定义filter实现代码: public class SmsAuthenticationfilter extends AbstractAuthenticationProcessingFilter { private boolean postOnly = true; public SmsAuthenticationfilter() { super(new AntPathRequestMatcher(SecurityConstants.APP_MOBILE_LOGIN_URL, "POST")); } [@Override](https://my.oschina.net/u/1162528) public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } Assert.hasText(SecurityConstants.MOBILE_NUMBER_PARAMETER, "mobile parameter must not be empty or null"); String mobile = request.getParameter(SecurityConstants.MOBILE_NUMBER_PARAMETER); String smsCode = request.ge+tParameter(SecurityConstants.MOBILE_VERIFY_CODE_PARAMETER); if (mobile == null) { mobile=""; } if(smsCode == null){ smsCode=""; } mobile = mobile.trim(); smsCode = smsCode.trim(); SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile,smsCode); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); }protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } } 2. Authentication: 在filter以及后面的认证都需要使用到自定义的Authentication对象,自定义Authentication对象可以根据UsernamePasswordAuthenticationToken进行仿写,实现AbstractAuthenticationToken抽象类。 自定义SmsAuthenticationToken: public class SmsAuthenticationToken extends AbstractAuthenticationToken { private final Object principal; private Object credentials; public SmsAuthenticationToken(Object principal,Object credentials ) { super(null); this.principal = principal; this.credentials=credentials; setAuthenticated(false); } public SmsAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) { super(null); this.principal = principal; this.credentials=credentials; setAuthenticated(true); } [@Override](https://my.oschina.net/u/1162528) public Object getCredentials() { return this.credentials=credentials; } [@Override](https://my.oschina.net/u/1162528) public Object getPrincipal() { return this.principal; } public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { if (isAuthenticated) { throw new IllegalArgumentException( "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); } super.setAuthenticated(false); } [@Override](https://my.oschina.net/u/1162528) public void eraseCredentials() { super.eraseCredentials(); } } 3.AuthenticationProvider AuthenticationProvider最终认证策略入口,短信方式验证需自定义AuthenticationProvider。可以根据AbstractUserDetailsAuthenticationProvider进行仿写,实现AuthenticationProvider以及MessageSourceAware接口。认证逻辑可以定义实现。 自定义AuthenticationProvider: public class SmsAuthenticationProvide implements AuthenticationProvider, MessageSourceAware { private UserDetailsService userDetailsService; private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); [@Override](https://my.oschina.net/u/1162528) public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } @Override public Authentication authenticate(Authentication authentication) { Assert.isInstanceOf(SmsAuthenticationToken.class, authentication, messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication; //将验证信息保存在SecurityContext以供UserDetailsService进行验证 SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(authenticationToken); String mobile = (String) authenticationToken.getPrincipal(); if (mobile == null) { throw new InternalAuthenticationServiceException("can't obtain user info "); } mobile = mobile.trim(); //进行验证以及获取用户信息 UserDetails user = userDetailsService.loadUserByUsername(mobile); if (user == null) { throw new InternalAuthenticationServiceException("can't obtain user info "); } SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(user, user.getAuthorities()); return smsAuthenticationToken; } @Override public boolean supports(Class<?> authentication) { return (SmsAuthenticationToken.class.isAssignableFrom(authentication)); } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } public UserDetailsService getUserDetailsService() { return userDetailsService; } } 4. UserDetailsService 在AuthenticationProvider最终认证策略入口,认证方式实现逻辑是在UserDetailsService。可以根据自己项目自定义认证逻辑。 自定义UserDetailsService: public class SmsUserDetailsService implements UserDetailsService { @Autowired private RedisUtil redisUtil; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //从SecurityContext获取认证所需的信息(手机号码、验证码) SecurityContext context = SecurityContextHolder.getContext(); SmsAuthenticationToken authentication = (SmsAuthenticationToken) context.getAuthentication(); if(!additionalAuthenticationChecks(username,authentication)){ return null; } //获取用户手机号码对应用户的信息,包括权限等 return new User("admin", "123456", Arrays.asList(new SimpleGrantedAuthority("admin"))); } public boolean additionalAuthenticationChecks(String mobile, SmsAuthenticationToken smsAuthenticationToken) { //获取redis中手机键值对应的value验证码 String smsCode = redisUtil.get(mobile).toString(); //获取用户提交的验证码 String credentials = (String) smsAuthenticationToken.getCredentials(); if(StringUtils.isEmpty(credentials)){ return false; } if (credentials.equalsIgnoreCase(smsCode)) { return true; } return false; } } 5.SecurityConfig 5.1 自定义Sms短信验证组件配置SecurityConfig 将自定义组件配置SecurityConfig中,可以根据AbstractAuthenticationFilterConfigurer(子类FormLoginConfigurer)进行仿写SmsAuthenticationSecurityConfig,主要进行以下配置: 将默认AuthenticationManager(也可以定义的)设置到自定义的filter过滤器中 将自定义的UserDetailsService设置到自定义的AuthenticationProvide中以供使用 将过滤器添加到过滤链路中,实施过滤操作。(一般以加在UsernamePasswordAuthenticationFilter前) 配置SmsAuthenticationSecurityConfig: @Component public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity http) throws Exception { //创建并配置好自定义SmsAuthenticationfilter, SmsAuthenticationfilter smsAuthenticationfilter = new SmsAuthenticationfilter(); smsAuthenticationfilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsAuthenticationfilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler()); smsAuthenticationfilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler()); //创建并配置好自定义SmsAuthenticationProvide SmsAuthenticationProvide smsAuthenticationProvide=new SmsAuthenticationProvide(); smsAuthenticationProvide.setUserDetailsService(userDetailsService); http.authenticationProvider(smsAuthenticationProvide); //将过滤器添加到过滤链路中 http.addFilterAfter(smsAuthenticationfilter, UsernamePasswordAuthenticationFilter.class); } @Bean public CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler() { return new CustomAuthenticationSuccessHandler(); } @Bean public CustomAuthenticationFailureHandler customAuthenticationFailureHandler() { return new CustomAuthenticationFailureHandler(); } } 5.2 SecurityConfig主配置 SecurityConfig主配置可以参照第二节Spring Security(二)--WebSecurityConfigurer配置以及filter顺序进行配置。 SecurityConfig主配置: @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig; @Autowired private CustomAuthenticationSuccessHandler authenticationSuccessHandler; @Autowired private CustomAuthenticationFailureHandler authenticationFailureHandler; @Override protected void configure(HttpSecurity http) throws Exception { http.headers().frameOptions().disable().and() .formLogin() .loginPage(SecurityConstants.APP_FORM_LOGIN_PAGE) //配置form登陆的自定义URL .loginProcessingUrl(SecurityConstants.APP_FORM_LOGIN_URL) .successHandler(authenticationSuccessHandler) .failureHandler(authenticationFailureHandler) .and() //配置smsAuthenticationSecurityConfig .apply(smsAuthenticationSecurityConfig) .and() //运行通过URL .authorizeRequests() .antMatchers(SecurityConstants.APP_MOBILE_VERIFY_CODE_URL, SecurityConstants.APP_USER_REGISTER_URL) .permitAll() .and() .csrf().disable(); } @Bean public ObjectMapper objectMapper(){ return new ObjectMapper(); } } 6.其他 6.1 redis RedisUtil工具类: @Component public class RedisUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } } redisConfig配置类: @Configuration public class RedisConfig { @Autowired private RedisProperties properties; @Bean @SuppressWarnings("all") @ConditionalOnClass(RedisConnectionFactory.class) public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } @Bean @Qualifier("redisConnectionFactory") public RedisConnectionFactory redisConnectionFactory(){ RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(); redisConfig.setHostName(properties.getHost()); redisConfig.setPort(properties.getPort()); redisConfig.setPassword(RedisPassword.of(properties.getPassword())); redisConfig.setDatabase(properties.getDatabase()); //redis连接池数据设置 JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder(); if (this.properties.getTimeout() != null) { Duration timeout = this.properties.getTimeout(); builder.readTimeout(timeout).connectTimeout(timeout); } RedisProperties.Pool pool = this.properties.getJedis().getPool(); if (pool != null) { builder.usePooling().poolConfig(this.jedisPoolConfig(pool)); } JedisClientConfiguration jedisClientConfiguration = builder.build(); //根据两个配置类生成JedisConnectionFactory return new JedisConnectionFactory(redisConfig,jedisClientConfiguration); } private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(pool.getMaxActive()); config.setMaxIdle(pool.getMaxIdle()); config.setMinIdle(pool.getMinIdle()); if (pool.getMaxWait() != null) { config.setMaxWaitMillis(pool.getMaxWait().toMillis()); } return config; } } 7.总结 可以根据短信验证登陆模式去实现类似的验证方式,可以结合本节的例子进行跟项目结合起来,减少开发时间。后续还有第三方登陆方式分析以案例。最后错误请评论指出! 最后麻烦各位看官点个赞,谢谢支持!!!

优秀的个人博客,低调大师

企业级 SpringBoot 教程 (二十四)springboot整合docker

这篇文篇介绍,怎么为 springboot程序构建一个docker镜像。docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。 准备工作 环境: linux环境或mac,不要用windows jdk 8 maven 3.0 docker 对docker一无所知的看docker教程。 创建一个springboot工程 引入web的起步依赖,创建一个 Controler: @SpringBootApplication @RestController public class SpringbootWithDockerApplication { @RequestMapping("/") public String home() { return "Hello Docker World"; } public static void main(String[] args) { SpringApplication.run(SpringbootWithDockerApplication.class, args); } } 将springboot工程容器化 Docker有一个简单的dockerfile文件作为指定镜像的图层。让我们先创建一个 dockerFile文件: src/main/docker/Dockerfile: FROM frolvlad/alpine-oraclejdk8:slim VOLUME /tmp ADD springboot-with-docker-0.0.1-SNAPSHOT.jar app.jar RUN sh -c 'touch /app.jar' ENV JAVA_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ] 我们通过maven 构建docker镜像。 在maven的pom目录,加上docker镜像构建的插件 <properties> <docker.image.prefix>springio</docker.image.prefix> </properties> <build> <plugins> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.11</version> <configuration> <imageName>${docker.image.prefix}/${project.artifactId}</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> </plugins> </build> 通过maven 命令: 第一步:mvn clean 第二步: mvn package docker:bulid ,如下: Step 2/6 : VOLUME /tmp —> Running in a98be3878053 —> 8286e98b54c5 Removing intermediate container a98be3878053 Step 3/6 : ADD springboot-with-docker-0.0.1-SNAPSHOT.jar app.jar —> c6ce13e50bbd Removing intermediate container a303a3058869 Step 4/6 : RUN sh -c ‘touch /app.jar’ —> Running in cf231afe700e —> 9a0ec8936c00 Removing intermediate container cf231afe700e Step 5/6 : ENV JAVA_OPTS “” —> Running in e192597fc881 —> 2cb0d73bbdb0 Removing intermediate container e192597fc881 Step 6/6 : ENTRYPOINT sh -c java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar —> Running in ab85f53fcdd8 —> 60fdb5c61692 Removing intermediate container ab85f53fcdd8 Successfully built 60fdb5c61692 [INFO] Built forezp/springboot-with-docker [INFO] ———————————————————————— [INFO] BUILD SUCCESS [INFO] ———————————————————————— [INFO] Total time: 01:45 min [INFO] Finished at: 2017-04-19T05:37:44-07:00 [INFO] Final Memory: 19M/48M //Spring Cloud大型企业分布式微服务云架构源码请加一七九一七四三三八零 [INFO] ————————————————————————

优秀的个人博客,低调大师

(十七) 整合spring cloud云架构 -消息驱动 Spring Cloud Stream

在使用spring cloud云架构的时候,我们不得不使用Spring cloud Stream,因为消息中间件的使用在项目中无处不在,我们公司后面做了娱乐方面的APP,在使用spring cloud做架构的时候,其中消息的异步通知,业务的异步处理都需要使用消息中间件机制。spring cloud的官方给出的集成建议(使用rabbit mq和kafka),我看了一下源码和配置,只要把rabbit mq集成,kafka只是换了一个pom配置jar包而已,闲话少说,我们就直接进入配置实施: 简介:Spring cloud Stream 数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。 使用工具: rabbit,具体的下载和安装细节我这里不做太多讲解,网上的实例太多了 创建commonservice-mq-producer消息的发送者项目,在pom里面配置stream-rabbit的依赖 <span style="font-size: 16px;"><!-- 引入MQ消息驱动的微服务包,引入stream只需要进行配置化即可,是对rabbit、kafka很好的封装 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency></span> 4.在yml文件里面配置rabbit mq <span style="font-size: 16px;">server: port: 5666 spring: application: name: commonservice-mq-producer profiles: active: dev cloud: config: discovery: enabled: true service-id: commonservice-config-server <span style="color: rgb(255, 0, 0);"># rabbitmq和kafka都有相关配置的默认值,如果修改,可以再次进行配置 stream: bindings: mqScoreOutput: destination: honghu_exchange contentType: application/json rabbitmq: host: localhost port: 5672 username: honghu password: honghu</span> eureka: client: service-url: defaultZone: http://honghu:123456@localhost:8761/eureka instance: prefer-ip-address: true</span> 5定义接口ProducerService <span style="font-size: 16px;">package com.honghu.cloud.producer; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.SubscribableChannel; public interface ProducerService { String SCORE_OUPUT = "mqScoreOutput"; @Output(ProducerService.SCORE_OUPUT) SubscribableChannel sendMessage(); }</span> 6 定义绑定 <span style="font-size: 16px;">package com.honghu.cloud.producer; import org.springframework.cloud.stream.annotation.EnableBinding; @EnableBinding(ProducerService.class) public class SendServerConfig { }</span> 7定义发送消息业务ProducerController <span style="font-size: 16px;">package com.honghu.cloud.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.honghu.cloud.common.code.ResponseCode; import com.honghu.cloud.common.code.ResponseVO; import com.honghu.cloud.entity.User; import com.honghu.cloud.producer.ProducerService; import net.sf.json.JSONObject; @RestController @RequestMapping(value = "producer") public class ProducerController { @Autowired private ProducerService producerService; /** * 通过get方式发送</span>对象<span style="font-size: 16px;"> * @param name 路径参数 * @return 成功|失败 */ @RequestMapping(value = "/sendObj", method = RequestMethod.GET) public ResponseVO sendObj() { User user = new User(1, "hello User"); <span style="color: rgb(255, 0, 0);">Message<User> msg = MessageBuilder.withPayload(user).build();</span> boolean result = producerService.sendMessage().send(msg); if(result){ return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_SUCCESS, false); } return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_FAILURE, false); } /** * 通过get方式发送字符串消息 * @param name 路径参数 * @return 成功|失败 */ @RequestMapping(value = "/send/{name}", method = RequestMethod.GET) public ResponseVO send(@PathVariable(value = "name", required = true) String name) { Message msg = MessageBuilder.withPayload(name.getBytes()).build(); boolean result = producerService.sendMessage().send(msg); if(result){ return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_SUCCESS, false); } return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_FAILURE, false); } /** * 通过post方式发送</span>json对象<span style="font-size: 16px;"> * @param name 路径参数 * @return 成功|失败 */ @RequestMapping(value = "/sendJsonObj", method = RequestMethod.POST) public ResponseVO sendJsonObj(@RequestBody JSONObject jsonObj) { Message<JSONObject> msg = MessageBuilder.withPayload(jsonObj).build(); boolean result = producerService.sendMessage().send(msg); if(result){ return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_SUCCESS, false); } return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_FAILURE, false); } } </span> 8创建commonservice-mq-consumer1消息的消费者项目,在pom里面配置stream-rabbit的依赖 <!-- 引入MQ消息驱动的微服务包,引入stream只需要进行配置化即可,是对rabbit、kafka很好的封装 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> 9在yml文件中配置: server: port: 5111 spring: application: name: commonservice-mq-consumer1 profiles: active: dev cloud: config: discovery: enabled: true service-id: commonservice-config-server <span style="color: rgb(255, 0, 0);">stream: bindings: mqScoreInput: group: honghu_queue destination: honghu_exchange contentType: application/json rabbitmq: host: localhost port: 5672 username: honghu password: honghu</span> eureka: client: service-url: defaultZone: http://honghu:123456@localhost:8761/eureka instance: prefer-ip-address: true 10定义接口ConsumerService package com.honghu.cloud.consumer; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface ConsumerService { <span style="color: rgb(255, 0, 0);">String SCORE_INPUT = "mqScoreInput"; @Input(ConsumerService.SCORE_INPUT) SubscribableChannel sendMessage();</span> } 11 定义启动类和消息消费 package com.honghu.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; import com.honghu.cloud.consumer.ConsumerService; import com.honghu.cloud.entity.User; @EnableEurekaClient @SpringBootApplication @EnableBinding(ConsumerService.class) //可以绑定多个接口 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } <span style="color: rgb(255, 0, 0);">@StreamListener(ConsumerService.SCORE_INPUT) public void onMessage(Object obj) { System.out.println("消费者1,接收到的消息:" + obj); }</span> } 12 分别启动commonservice-mq-producer、commonservice-mq-consumer1 13 通过postman来验证消息的发送和接收 可以看到接收到了消息,下一章我们介绍mq的集群方案。 到此,整个消息中心方案集成完毕欢迎大家和我一起学习spring cloud构建微服务云架构,我这边会将近期研发的spring cloud微服务云架构的搭建过程和精髓记录下来,帮助更多有兴趣研发spring cloud框架的朋友,大家来一起探讨spring cloud架构的搭建过程及如何运用于企业项目。

优秀的个人博客,低调大师

(十八) 整合spring cloud云架构 -后台管理基础功能简介

项目介绍 鸿鹄云开发平台是一个大型分布式、微服务、云架构、面向企业的 JavaEE体系快速研发平台,基于模块化、服务化、原子化、热插拔的设计思想,使用成熟领先的无商业限制的主流开源技术构建。 采用服务化的组件开发模式,可实现复杂的业务功能。使用Maven进行项目的构建管理,采用Jenkins进行持续集成,主要定位于大型分布式企业系统或大型分布式互联网产品的架构。使用当前最流行最先进的Spring Cloud技术实现服务组件化及管理,真正为企业打造分布式微服务云架构平台。 使用技术(技术使用太多,这里只列了一部分) SOA服务框架:SpringCloud 、SpringBoot、RestFul等 分布式缓存:Redis 模块化管理:Maven 数据库连接池:Alibaba Druid 核心框架:Spring framework、SpringBoot 持久层框架:MyBatis 安全框架:Apache Shiro 服务端验证:Hibernate Validator 任务调度:quartz 日志管理:SLF4J 1.7、Log4j 客户端验证:JQuery Validation 动态页签:easyuitab 前端框架:Bootstrap、Vue 设计思想 分布式、微服务、云架构 JAVA语言开发、跨平台、高性能、高可用、安全、服务化、模块化、组件化、驱动式开发模式 平台基础功能 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 权限管理:对系统中经常使用的一些较为固定的数据进行维护等。 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 部门管理:配置系统组织机构,树结构展现,可随意调整上下级。 日志管理:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 连接池监视:监视当期系统数据库连接池状态,分析系统性能瓶颈。 源码结构 注册中心 部分功能截图 欢迎大家和我一起学习spring cloud构建微服务云架构,我这边会将近期研发的spring cloud微服务云架构的搭建过程和精髓记录下来,帮助更多有兴趣研发spring cloud框架的朋友,大家来一起探讨spring cloud架构的搭建过程及如何运用于企业项目。

优秀的个人博客,低调大师

整合SpringCloud微服务分布式云架构技术点

spring cloud本身提供的组件就很多,但我们需要按照企业的业务模式来定制企业所需要的通用架构,那我们现在需要考虑使用哪些技术呢? 下面我针对于spring cloud微服务分布式云架构做了以下技术总结,希望可以帮助到大家: View: H5、Vue.js、Spring Tag、React、angularJs Spring Boot/Spring Cloud:Zuul、Ribbon、Feign、Turbine、Hystrix、Oauthor2、Sleuth、API Gateway、Spring Cloud、Config Eureka、SSO、Spring Cloud、BUS、Turbine、Zipkin、Cache、Spring Cloud Admin、API Gateway、ELK Spring Cloud Security、 Spring Cloud Stream Component:RoketMQ、Kafka、MongoDB、OSS、Redis、Swagger、Zuul、Label、BASE、Charts、Utils DAO: Spring Data、Mybatis、OSS、 DTO Data Storage:RDBS DFS、NOSQL/Hadoop Infrastructure:LogBack、BUS、Jenkins、Zipkin、Druid、Swagger、Docker

优秀的个人博客,低调大师

SSH框架整合遇到的错误——org.springframework.beans.NotWritablePropertyException:

提示错误信息: Unable to instantiate Action, userAction, defined for 'user_registPage' in namespace '/'Error creating bean with name 'userAction' defined in class path resource [applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'userService' of bean class [com.itcast.shop.user.action.UserAction]: Bean property 'userService' is not writable or has an invalid setter method. Did you mean 'userservive'? - action - file:/D:/Program%20Files%20(x86)/apache-tomcat-9.0.1/webapps/shop/WEB-INF/classes/struts.xml:36:56 com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:320) com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:401) com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:201) org.apache.struts2.factory.StrutsActionProxy.prepare(StrutsActionProxy.java:57) org.apache.struts2.factory.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:32) com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:60) org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564) org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79) org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:141) 出现此问题的原因是在ApplicationContext.xml中配置有问题,里面的bean的属性名称用 与 注入的类名称不一致 applicationContext.xml <!-- 用户模块的Action --> <bean id="userAction" class="com.itcast.shop.user.action.UserAction" scope="prototype"> <property name="userService" ref="userService"/> </bean> userAction private UserDaouserdao; private UserService userService; public void setUserservive(UserService userService) { this.userService = userService; } 红色部分名称要一致。

优秀的个人博客,低调大师

SSH框架整合遇到的错误——Hibernate查询语句出现错误

在调试前台注册界面,填写注册信息,用户名Ajax异步验证时报错,报错文件在Dao文件的查询语句中。 报错信息: java.lang.IllegalArgumentException: org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead : from com.itcast.shop.user.vo.User where username = ? [from com.itcast.shop.user.vo.User where username = ?] at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:133) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164) at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:713) at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:103) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 错误原因:find方法过时,导致HQL语句格式必须改变才能使用。 将HQL语句中的"?"改为JPA-style: String hql="from User where username = ?0"; 这样就可以正确查询到信息了。

优秀的个人博客,低调大师

windows下elasticsearch配置及spring boot 简单demod的 整合

学习过程: elasticsearch 下载安装 elasticsearch-head 安装 spring boot 下elasticsearch的配置 使用ElasticsearchRepository实现增删改查(ElasticsearchRepository,elasticsearchTemplate) 如何优雅的使用FunctionScoreQueryBuilder 测试 一、elasticsearch 下载安装:ElasticSearch官网:http://www.elasticsearch.org 在安装Elasticsearch之前我们需要先安装jdk的环境,这些都是老生常谈,我们不去多加叙述,具体的安装步骤我们可以参考https://www.cnblogs.com/ljhdo/p/4887557.html,这里有详细的Elasticsearch及jdk安装步骤。安装好之后我们可以找到安装目录bin下的批处理文件来启动项目. 看到这样的界面后我们可以在浏览器里输入http://localhost:9200/可以看到返回了一段json,其中对外服务的http端口,默认为9200,9300是客户端的端口。在这里elasticsearch我们就安装完了。 { "name": "node-1", "cluster_name": "my-application", "cluster_uuid": "YWYqGhDnSE-z3pbVDEs8rQ", "version": { "number": "6.3.0", "build_flavor": "default", "build_type": "zip", "build_hash": "424e937", "build_date": "2018-06-11T23:38:03.357887Z", "build_snapshot": false, "lucene_version": "7.3.1", "minimum_wire_compatibility_version": "5.6.0", "minimum_index_compatibility_version": "5.0.0" }, "tagline": "You Know, for Search" } 二、elasticsearch-head 安装 elasticsearch安装完后我们需要安装head插件管理我们的elasticsearch,上面链接教程中elasticsearch使用的是2.4.4的版本,而我用的是6.3.0的版本,在cmd中使用es命令的方式已经不可用了。我们需要自己区去官网下载安装包,在这之前还需要先安装node.js和grunt,参考https://www.cnblogs.com/Onlywjy/p/Elasticsearch.html我们能很快的完成elasticsearch及head的安装和配置。安装完成后我们可以通过cmd进入到head的安装目录通过“npm run start ”来启动head插件,在浏览器中输入”http://localhost:9100“来访问。 三、spring boot 下配置 pom依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> application.yml spring: data: elasticsearch: cluster-name: my-application #elasticsearch/config文件下elasticsearch.yml中设置的cluster.name cluster-nodes: 127.0.0.1:9300 #客户端端口,启动elasticsearch时默认为9300 四、使用ElasticsearchRepository实现增删改查 参考https://blog.csdn.net/larger5/article/details/79777319,偷懒的同学,可以直接看下面,我们完成了pojo,dao,controller的编写,由于只是做了个demo就没有使用service层去规范。在clone链接中代码时候我们会遇到一些错误,下面我们着手解决这些错误。 pojo import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; /** * @Author: gaofeng_peng * @Date: 2018/6/24 10:44 */ @Document(indexName = "product", type = "book") public class Book { @Id String id; String name; String message; String type; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getType() { return type; } public void setType(String type) { this.type = type; } 在上述代码中@Document注解中indexName指的是索引,可以理解成mysql中数据库 ,type既对应的是数据表。 dao public interface BookDao extends ElasticsearchRepository<Book, String> { Book findBooksById(String id); void deleteById(String id); } 参考的文档中,dao层没有写接口,在后面的实现中会报错,忖度作者的用意controller下getBookById中bookDao.findOne() 方法 对应了findBooksById,insertBook中bookDao.delete() 对应deleteById,相信这么简单大家都能看出来。 controller package com.bookstore.controller.backend; import com.bookstore.dao.BookDao; import com.bookstore.pojo.Book; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryStringQueryBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * @Author: gaofeng_peng * @Date: 2018/6/24 11:02 */ @RestController @RequestMapping("book") public class BookController { @Autowired private BookDao bookDao; /** * 1、查 id * * @param id * @return */ @GetMapping("/get/{id}") public Book getBookById(@PathVariable String id) { return bookDao.findBooksById(id); } /** * 2、查 ++:全文检索(根据整个实体的所有属性,可能结果为0个) * * @param q * @return */ @GetMapping("/select/{q}") public List<Book> testSearch(@PathVariable String q) { QueryStringQueryBuilder builder = new QueryStringQueryBuilder(q); Iterable<Book> searchResult = bookDao.search(builder); Iterator<Book> iterator = searchResult.iterator(); List<Book> list = new ArrayList<Book>(); while (iterator.hasNext()) { list.add(iterator.next()); } return list; } /** * 3、查 +++:分页、分数、分域(结果一个也不少) * * @param page * @param size * @param q * @return */ @GetMapping("/{page}/{size}/{q}") public List<Book> searchCity(@PathVariable Integer page, @PathVariable Integer size, @PathVariable String q) { // 分页参数 Pageable pageable = new PageRequest(page, size); FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchQuery("name", q), ScoreFunctionBuilders.weightFactorFunction(1000)), new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchQuery("message", q), ScoreFunctionBuilders.weightFactorFunction(1000)) }; FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions); // 分数、分页 SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable) .withQuery(functionScoreQueryBuilder).build(); Page<Book> searchPageResults = bookDao.search(searchQuery); return searchPageResults.getContent(); } /** * 4、增 * * @param book * @return */ @PostMapping("/insert") public Book insertBook(Book book) { bookDao.save(book); return book; } /** * 5、删 id * * @param id * @return */ @DeleteMapping("/delete/{id}") public Book insertBook(@PathVariable String id) { Book book = bookDao.findBooksById(id); bookDao.deleteById(id); return book; } /** * 6、改 * * @param book * @return */ @PutMapping("/update") public Book updateBook(Book book) { bookDao.save(book); return book; } } 在这里我们要着重讲一下参考文档中的searchCity 方法,下面是作者的写法: /** * 3、查 +++:分页、分数、分域(结果一个也不少) * @param page * @param size * @param q * @return * @return */ @GetMapping("/{page}/{size}/{q}") public List<Book> searchCity(@PathVariable Integer page, @PathVariable Integer size, @PathVariable String q) { // 分页参数 Pageable pageable = new PageRequest(page, size); // 分数,并自动按分排序 FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery() .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", q)), ScoreFunctionBuilders.weightFactorFunction(1000)) // 权重:name 1000分 .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("message", q)), ScoreFunctionBuilders.weightFactorFunction(100)); // 权重:message 100分 // 分数、分页 SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable) .withQuery(functionScoreQueryBuilder).build(); Page<Book> searchPageResults = bookDao.search(searchQuery); return searchPageResults.getContent(); } 大家注意下红色的部分,由于我们在配置依赖时候没有指定elasticsearch的版本,现在如果还是直接clone上面的依赖的话会发现已经没有add的方法了,一种方式去指定版本,都走到这一步了,我们采取另一种方式使用 FunctionScoreQueryBuilder functionScoreQuery(ScoreFunctionBuilder function)方法,具体看下面 public List<Book> searchCity(@PathVariable Integer page, @PathVariable Integer size, @PathVariable String q) { // 分页参数 Pageable pageable = new PageRequest(page, size); FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchQuery("name", q), ScoreFunctionBuilders.weightFactorFunction(1000)), new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchQuery("message", q), ScoreFunctionBuilders.weightFactorFunction(1000)) }; FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions); // 分数、分页 SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable) .withQuery(functionScoreQueryBuilder).build(); Page<Book> searchPageResults = bookDao.search(searchQuery); return searchPageResults.getContent(); } 上面我们使用的是:SpringData封装,直接在 dao 接口继承 ElasticsearchRepository的方式,作者很全面还提供了elasticsearchTemplate的方式, package com.bookstore.controller.backend; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @Author: gaofeng_peng * @Date: 2018/6/24 10:58 */ @RestController @RequestMapping("/template") public class BookControllerTemplate { @Autowired ElasticsearchTemplate elasticsearchTemplate; /** * 查询所有 * @throws Exception */ @GetMapping("/all") public List<Map<String, Object>> searchAll() throws Exception { //这一步是最关键的 Client client = elasticsearchTemplate.getClient(); // @Document(indexName = "product", type = "book") SearchRequestBuilder srb = client.prepareSearch("product").setTypes("book"); SearchResponse sr = srb.setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(); // 查询所有 SearchHits hits = sr.getHits(); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); for (SearchHit hit : hits) { Map<String, Object> source = hit.getSource(); list.add(source); System.out.println(hit.getSourceAsString()); } return list; } } 到此位置简单的增删改查就完成了,还需一点注意的是作者@RestController什么的没加,记得加上。。。。。 五、如何优雅的使用FunctionScoreQueryBuilder 福利链接:https://www.programcreek.com/java-api-examples/index.php?api=org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder,找了老半天,必须给我个 上面的是FunctionScoreQueryBuilder的Java代码示例,总有那么一种方式适合你。 六、测试 在这里我们安装了google 的restlet client 插件来方便测试,当然,也可以使用head插件上的复合查询来测试。 图片比较大这里我们只放部分的测试结果,就不一一列举了,至此整个项目就完成了,有什么不足,欢迎大家指点。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册