http://www.nwn2toolset.dayjo.org/ToolsetTuts/conversations/writingconvos.html
https://twinery.org/
http://www.inklestudios.com/inklewriter/
http://www.nwn2toolset.dayjo.org/ToolsetTuts/conversations/writingconvos.html
https://twinery.org/
http://www.inklestudios.com/inklewriter/
最近看了vuejs,看到了vue-auth中一个jwt-auth,发现几年不关注,前端完全是全新的领域了。
这里列下jwt相关的资源,供快速了解jwt.
https://jwt.io/introduction/
https://github.com/szerhusenBC/jwt-spring-security-demo
https://www.toptal.com/java/rest-security-with-jwt-spring-security-and-java
spring boot security如何集成JWT.
java相关的jwt库太多了。
apache有oltu
spring有spring-security-jwt
还有其它一些第三方开源库
1、服务器端添加 jwt 支持:
配置spring security
package com.elex.gm.spring; import java.security.SecureRandom; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.web.authentication.UsernamePasswordAuthenticationFilter; import com.elex.gm.service.UserManager; import com.elex.gm.spring.jwt.JWTAuthenticationFilter; import com.elex.gm.spring.jwt.JWTLoginFilter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public BCryptPasswordEncoder passwordEncoder() throws Exception { return passwordEncoder; } public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/static/**").antMatchers("/hello/**").antMatchers("/api/open/**"); }; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/api/gm/**").hasAnyRole("ADMIN", "USER") .antMatchers("/api/user/**").hasAnyRole("USER") .and() .formLogin() .loginProcessingUrl("/api/user/login") .permitAll() .and() .logout() // .logoutUrl("/api/open/logout") .and() .httpBasic() .and() .addFilterBefore(new JWTLoginFilter("/api/user/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); http.csrf().disable(); http.headers().frameOptions().disable(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userManager).passwordEncoder(passwordEncoder); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { } @Autowired private UserManager userManager; private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(16, new SecureRandom("j^E1)y*)HL2gT,es+ODa!0+j^L1I3+6I".getBytes())); }
JWTLoginFilter:
package com.elex.gm.spring.jwt; import java.io.IOException; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import com.alibaba.fastjson.JSON; import com.elex.gm.model.User; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { public JWTLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) { super(defaultFilterProcessesUrl); setAuthenticationManager(authenticationManager); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null) { username = ""; } else { username = username.trim(); } if (password == null) { password = ""; } else { password = password.trim(); } return getAuthenticationManager() .authenticate(new UsernamePasswordAuthenticationToken(username, password, Lists.newArrayList())); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { User user = (User) authResult.getPrincipal(); Map<String, Object> tokenMap = Maps.newHashMap(); tokenMap.put("id", user.getId()); tokenMap.put("name", user.getUsername()); tokenMap.put("roles", user.getRoles()); Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), JWTKey.hmac); String token = jwt.getEncoded(); response.setHeader("Authorization", token); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { response.setStatus(HttpStatus.UNAUTHORIZED.value()); } }
JWTAuthentication:
package com.elex.gm.spring.jwt; import java.io.IOException; import java.util.List; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; import org.springframework.web.filter.GenericFilterBean; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; public class JWTAuthenticationFilter extends GenericFilterBean { private static final String JWT_HEADER = "Authorization"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { String token = ((HttpServletRequest) request).getHeader(JWT_HEADER); Jwt jwt = JwtHelper.decodeAndVerify(token, JWTKey.hmac); JSONObject json = JSON.parseObject(jwt.getClaims()); String username = json.getString("name"); String roles = json.getString("roles"); List<SimpleGrantedAuthority> list = Lists.newArrayList(); if (!StringUtils.isEmpty(roles)) { for (String role : roles.split(",")) { if (!StringUtils.isEmpty(role)) { list.add(new SimpleGrantedAuthority("ROLE_" + role)); } } } Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, list); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { // e.printStackTrace(); } chain.doFilter(request, response); } }
收到一个需求,需要判断IP是否来自中国。
判断IP来源,很早之前自己也感兴趣,一直没有去了解如何实现。
今天正好搜索了一下相关的文章。
亚洲所有相关分配的IP段可以从 http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest 查询。
从里面解释后即可实现判断IP是否为中国。
没有找到java的代码,就自己写了一份,供其它朋友使用。
将下载下来的 delegated-apnic-latest 放在 classpath下即可。
import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.assertj.core.util.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Maps; public class IpUtil { private static final String FILE_NAME = "delegated-apnic-latest"; // 只存放属于中国的ip段 private static Map<Integer, List<int[]>> chinaIps = Maps.newHashMap(); static { init(); } public static void init() { try { // ip格式: add1.add2.add3.add4 // key为 : add1*256+add2 // value为int[2]: int[0]存的add3*256+add4的开始ip int[4]存的结束ip Map<Integer, List<int[]>> map = Maps.newHashMap(); InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(FILE_NAME); List<String> lines = IOUtils.readLines(input, StandardCharsets.UTF_8); for (String line : lines) { if (line.startsWith("apnic|CN|ipv4|")) { // 只处理属于中国的ipv4地址 String[] strs = line.split("\\|"); String ip = strs[3]; String[] add = ip.split("\\."); int count = Integer.valueOf(strs[4]); int startIp = Integer.parseInt(add[0]) * 256 + Integer.parseInt(add[1]); while (count > 0) { if (count >= 65536) { // add1,add2 整段都是中国ip chinaIps.put(startIp, Collections.EMPTY_LIST); count -= 65536; startIp += 1; } else { int[] ipRange = new int[2]; ipRange[0] = Integer.parseInt(add[2]) * 256 + Integer.parseInt(add[3]); ipRange[1] = ipRange[0] + count; count -= count; List<int[]> list = map.get(startIp); if (list == null) { list = Lists.newArrayList(); map.put(startIp, list); } list.add(ipRange); } } } } chinaIps.putAll(map); } catch (Exception e) { logger.error("ERROR", e); } } public static boolean isChinaIp(String ip) { if (StringUtils.isEmpty(ip)) { return false; } String[] strs = ip.split("\\."); if (strs.length != 4) { return false; } int key = Integer.valueOf(strs[0]) * 256 + Integer.valueOf(strs[1]); List<int[]> list = chinaIps.get(key); if (list == null) { return false; } if (list.size() == 0) { // 整段都是中国ip return true; } int ipValue = Integer.valueOf(strs[2]) * 256 + Integer.valueOf(strs[3]); for (int[] ipRange : list) { if (ipValue >= ipRange[0] && ipValue <= ipRange[1]) { return true; } } return false; } private static final Logger logger = LoggerFactory.getLogger(IpUtil.class); }
如果扩展这个代码后,可以增加判断IP属于哪个国家。
如果在判断IP属于哪个城市,可能得需要 http://dev.maxmind.com/zh-hans/geoip/geoip2/
参考:
https://www.zhihu.com/question/19794443
http://xixitalk.github.io/blog/2013/03/11/func-is-china-ip/
写脚本时经常需要获取当前执行的脚本所在的目录。
我最早使用的是:
shellPath=`echo $PWD/``echo ${0%/*}`
今天在做一个远程执行脚本时才发现,如果执行脚本时使用绝对路径,上面会得到错误的路径,现修改如下,可同时支持相对路径和绝对路径执行脚本:
shellPath=`echo $PWD/``echo ${0%/*}` # process absolute path shellPath1=`echo $PWD/` shellPath2=`echo ${0%/*}` if [ ${shellPath2:0:1} == '/' ] ; then shellPath=${shellPath2} fi
今天看代码时看到一个之前不知道的技巧,可以扩展Unity的GameObject类,为GameObject添加自己定义的方法:
using UnityEngine; using System.Collections; public static class GameObjectEx { public static T MakeSureComponent<T>(this GameObject gameObject) where T : Component { T t = gameObject.GetComponent<T>(); if (t == null) { t = gameObject.AddComponent<T>(); } return t; } }
这个类添加在工程中后,所以的gameObject上都可以直接调用MakeSureCompoent这个方法,真是超级便捷.
this.m_footstepHandler = base.gameObject.MakeSureComponent<FootstepHandler>();
同理,Unity自带的其它类应该也是可以进行扩展的。
查找了下是哪个天才想到的这个办法,google找到最早的一篇是这个:
https://forum.unity3d.com/threads/howto-add-method-to-the-gameobject-class-ie-add-custom-variables-to-every-instance.89791/
手机上的聊天:有语音SDK,可以语音文本互转。也有语音SDK直接支持自建语音频道。
有文本翻译API,可以翻译为多国语种文字:https://www.microsoft.com/en-us/translator/home.aspx
各种翻译,目前已知的商业API有微软的,百度的,行云的,另外还它一些商业API提交翻译功能。
微软的 MicrosoftTranslatorAPI 可以检测出使用的语种。
一个朋友联系我说服务器被当做肉鸡了,在 /etc/init.d 目录有几个脚本无法删除,删除后自动生成。
联系云服务器技术客服,回复说是被当做肉鸡了。
我连上服务器查看了下那个脚本:
#chkconfig: 12345 90 90 #description: acdnfhruvx #BEGIN INIT INFO #Provides: acdnfhruvx #Required-Start: #Required-Stop: #Default-Start: 1 2 3 4 5 #Default-Stop: #Short-Description: acdnfhruvx #END INIT INFO case $1 in start) /bin/acdnfhruvx ;; stop) ;; *) /bin/acdnfhruvx ;; esac
感觉没有任何有用的线索。
查看 conrtab -e ,也仅有一个正常的业务逻辑脚本。
本来以为他是常驻内存的进程来定时检测文件是否被删除,但查看history, ps 等也没有得到任何有用的线索。
打开 /etc/crontab 脚本时,发现异常了:
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed */3 * * * * root /etc/cron.hourly/gcc4.sh
这个 gcc4.sh 是什么鬼,而且每3分钟执行一次,如此高频率就不仅仅是可疑,简单就是在自己脸上贴上我是木马。
打开 /etc/cron.hourly/gcc4.sh 后就很明了了:自动执行一个二进程代码文件。
把gcc4.sh 文件备份后修改名字,再去 /etc/init.d 目录下清除可疑的自启动脚本,删除后没有重新创建出来。
再去 修复 /etc/crontab ,把最后一行删除。把 gcc4.sh 删除,把里面提到的 so 文件删除。
OK,结束。
学习到如果需要显示一个圆型的图片,需要自己来写shader,这里是我学习写的第一个shader。
以后会边学习边将写的shader记录下来,也可以不断补充自己学习能力。
Shader "Custom/Circle2" { Properties{ _Color("Color", Color) = (1,0,0,0) } SubShader{ Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True" } Pass{ ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" fixed4 _Color; // low precision type is usually enough for colors struct fragmentInput { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; fragmentInput vert(appdata_base v) { fragmentInput o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord.xy - fixed2(0.5,0.5); return o; } fixed4 frag(fragmentInput i) : SV_Target{ float distance = sqrt(pow(i.uv.x, 2) + pow(i.uv.y,2)); if (distance > 0.5f) { if (distance < 0.51f) { return fixed4(_Color.r* distance, _Color.g* distance, _Color.b* distance, 0.6f); } else { return fixed4(0, 0, 0, 0); } } else { return fixed4(_Color.r * distance, _Color.g * distance, _Color.b * distance,1); } } ENDCG } } }
自己之前写的在java服务器上使用导航网格寻路的库。
底层使用c++提供接口,java层通过jni调用c++接口。
https://github.com/zhukunqian/unity_navmesh_for_java
更多信息直接在github上交流吧。
https://bitbucket.org/Unity-Technologies/memoryprofiler
近期评论