作者存档: 朱坤乾 - 第3页

三个story工具

http://www.nwn2toolset.dayjo.org/ToolsetTuts/conversations/writingconvos.html

https://twinery.org/

 

http://www.inklestudios.com/inklewriter/

Json web token (JWT) 相关介绍

最近看了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来源,很早之前自己也感兴趣,一直没有去了解如何实现。

今天正好搜索了一下相关的文章。

亚洲所有相关分配的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类

今天看代码时看到一个之前不知道的技巧,可以扩展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记录下来,也可以不断补充自己学习能力。

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 recast navmesh寻路库

自己之前写的在java服务器上使用导航网格寻路的库。

底层使用c++提供接口,java层通过jni调用c++接口。

https://github.com/zhukunqian/unity_navmesh_for_java

更多信息直接在github上交流吧。

unity官方出的内存查看工具

https://bitbucket.org/Unity-Technologies/memoryprofiler


Warning: Use of undefined constant XML - assumed 'XML' (this will throw an Error in a future version of PHP) in /opt/wordpress/wp-content/plugins/wp-syntaxhighlighter/wp-syntaxhighlighter.php on line 1048