代码地址以下:
http://www.demodashi.com/demo/12736.htmljavascript
具体的登陆说明查看 小程序官方APIhtml
使用idea做为开发工具,由gradle构建项目,搭建springboot项目,对这块儿不熟悉的能够自行去学习,此处很少赘述。下面是核心的配置文件。application.yml中配置springboot默认的参数,application.properties配置自定义的参数,能够统一配置在一个文件中,依据我的习惯。java
buidle.gradle配置mysql
buildscript { ext { springBootVersion = '1.5.10.RELEASE' } repositories { mavenLocal() maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'org.springframework.boot' group = 'xin.yangmj' version = '1.0.1' sourceCompatibility = 1.8 repositories { mavenLocal() maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-cache') compile('org.springframework.boot:spring-boot-starter-data-redis') compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1') compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-web') compile('mysql:mysql-connector-java') compile('org.springframework.security:spring-security-test') testCompile('org.springframework.boot:spring-boot-starter-test') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' }
application.ymlweb
logging: level: root: DEBUG spring: datasource: url: jdbc:mysql://localhost/remindme?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 username: root password: root driver-class-name: com.mysql.jdbc.Driver redis: host: localhost password: port: 6379 mybatis: mapperLocations: classpath:mapper/*.xml configuration: mapUnderscoreToCamelCase: true default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
application.propertiesredis
# JWT相关配置 jwt.header=Authorization # 过时时间 jwt.expiration=864000 # 注意有一个空格 jwt.tokenHead=Bearer # wechat Auth auth.wechat.sessionHost=https://api.weixin.qq.com/sns/jscode2session auth.wechat.appId=*** auth.wechat.secret=*** auth.wechat.grantType=authorization_code
WebSecurityConfig.javaspring
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint unauthorizedHandler; @Bean public ThirdSessionAuthFilter authenticationTokenFilterBean() throws Exception { return new ThirdSessionAuthFilter(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // 因为使用的是JWT,咱们这里不须要csrf .csrf().disable() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,因此不须要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() // 容许对test的无受权访问 .antMatchers(HttpMethod.GET, "/test").permitAll() // 对于获取token的rest api要容许匿名访问 .antMatchers("/auth").permitAll(); // 添加本地地三方session filter httpSecurity .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); // 禁用缓存 httpSecurity.headers().cacheControl(); } }
ThirdSessionAuthFilter.javasql
@Component public class ThirdSessionAuthFilter extends OncePerRequestFilter { @Value("${jwt.header}") private String tokenHeader; @Value("${jwt.tokenHead}") private String tokenHead; @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private ConsumerMapper consumerMapper; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //获取请求头部分的Authorization String authHeader = request.getHeader(this.tokenHeader); //若是请求路径为微信通知后台支付结果则不须要token(以后会在具体的controller中,对双方签名进行验证防钓鱼) String url = request.getRequestURI().substring(request.getContextPath().length()); if (url.equals("/auth") || url.equals("/test")) { chain.doFilter(request, response); return; } if (null == authHeader || !authHeader.startsWith("Bearer")) { throw new RuntimeException("非法访问用户"); } // The part after "Bearer " final String thirdSessionId = authHeader.substring(tokenHead.length()); String wxSessionObj = stringRedisTemplate.opsForValue().get(thirdSessionId); if (StringUtils.isEmpty(wxSessionObj)) { throw new RuntimeException("用户身份已过时"); } // 设置当前登陆用户 try (AppContext appContext = new AppContext(wxSessionObj.substring(wxSessionObj.indexOf("#") + 1))) { chain.doFilter(request, response); } } }
AppContext.java数据库
public class AppContext implements AutoCloseable { private static final ThreadLocal<String> CURRENT_CONSUMER_WECHAT_OPENID = new ThreadLocal<>(); public AppContext(String wechatOpenid) { CURRENT_CONSUMER_WECHAT_OPENID.set(wechatOpenid); } @Override public void close() { CURRENT_CONSUMER_WECHAT_OPENID.remove(); } public static String getCurrentUserWechatOpenId() { return CURRENT_CONSUMER_WECHAT_OPENID.get(); } }
JwtAuthenticationEntryPoint.javaapache
@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } }
WechatAuthProperties.java
@Component public class WechatAuthProperties { @Value("${auth.wechat.sessionHost}") private String sessionHost; @Value("${auth.wechat.appId}") private String appId; @Value("${auth.wechat.secret}") private String secret; @Value("${auth.wechat.grantType}") private String grantType; //省略getter setter }
public class AccountDto { private Long id; private String username; private Long phone; private Gender gender; private String vcode; private String password; private String promotionCode; private String InvitationCode; private String clientAssertion; private String code; //省略 getter setter }
Consumer.java
public class Consumer { private Long id; private String username; private String wechatOpenid; private Long phone; private String nickname; private String avatarUrl; private Gender gender; private String email; private Long lastLoginTime; private Boolean deleted; private Long createdBy; private Long createdAt; private Long updatedBy; private Long updatedAt; // 省略 gettter setter }
Gender.java
public enum Gender { UNKNOW(0, "未知"), MAN(1, "先生"), WOMAN(2, "女士"); private Byte value; private String name; Gender(int value, String name) { this.value = (byte)value; this.name = name; } public Byte getValue() { return this.value; } public String getName() { return this.name; } }
@RestController public class AuthEndpoint { @Value("${jwt.header}") private String tokenHeader; @Value("${jwt.tokenHead}") private String tokenHead; @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private WechatService wechatService; @GetMapping("/test") public String test() { return "test_success"; } @GetMapping("/testAuth") public String testAuth() { return "testAuth_success"; } @PostMapping("/auth") public ResponseEntity<WechatAuthenticationResponse> createAuthenticationToken(@RequestBody AccountDto accountDto) throws AuthenticationException { WechatAuthenticationResponse jwtResponse = wechatService.wechatLogin(accountDto.getCode()); return ResponseEntity.ok(jwtResponse); } @PostMapping("/updateConsumerInfo") public void updateConsumerInfo(@RequestBody Consumer consumer) { wechatService.updateConsumerInfo(consumer); } }
@Service public class WechatService { private static final Logger LOGGER = LoggerFactory.getLogger(WechatService.class); @Autowired private ConsumerMapper consumerMapper; /** * 服务器第三方session有效时间,单位秒, 默认1天 */ private static final Long EXPIRES = 86400L; private RestTemplate wxAuthRestTemplate = new RestTemplate(); @Autowired private WechatAuthProperties wechatAuthProperties; @Autowired private StringRedisTemplate stringRedisTemplate; public WechatAuthenticationResponse wechatLogin(String code) { WechatAuthCodeResponse response = getWxSession(code); String wxOpenId = response.getOpenid(); String wxSessionKey = response.getSessionKey(); Consumer consumer = new Consumer(); consumer.setWechatOpenid(wxOpenId); loginOrRegisterConsumer(consumer); Long expires = response.getExpiresIn(); String thirdSession = create3rdSession(wxOpenId, wxSessionKey, expires); return new WechatAuthenticationResponse(thirdSession); } public WechatAuthCodeResponse getWxSession(String code) { LOGGER.info(code); String urlString = "?appid={appid}&secret={srcret}&js_code={code}&grant_type={grantType}"; String response = wxAuthRestTemplate.getForObject( wechatAuthProperties.getSessionHost() + urlString, String.class, wechatAuthProperties.getAppId(), wechatAuthProperties.getSecret(), code, wechatAuthProperties.getGrantType()); ObjectMapper objectMapper = new ObjectMapper(); ObjectReader reader = objectMapper.readerFor(WechatAuthCodeResponse.class); WechatAuthCodeResponse res; try { res = reader.readValue(response); } catch (IOException e) { res = null; LOGGER.error("反序列化失败", e); } LOGGER.info(response); if (null == res) { throw new RuntimeException("调用微信接口失败"); } if (res.getErrcode() != null) { throw new RuntimeException(res.getErrmsg()); } res.setExpiresIn(res.getExpiresIn() != null ? res.getExpiresIn() : EXPIRES); return res; } public String create3rdSession(String wxOpenId, String wxSessionKey, Long expires) { String thirdSessionKey = RandomStringUtils.randomAlphanumeric(64); StringBuffer sb = new StringBuffer(); sb.append(wxSessionKey).append("#").append(wxOpenId); stringRedisTemplate.opsForValue().set(thirdSessionKey, sb.toString(), expires, TimeUnit.SECONDS); return thirdSessionKey; } private void loginOrRegisterConsumer(Consumer consumer) { Consumer consumer1 = consumerMapper.findConsumerByWechatOpenid(consumer.getWechatOpenid()); if (null == consumer1) { consumerMapper.insertConsumer(consumer); } } public void updateConsumerInfo(Consumer consumer) { Consumer consumerExist = consumerMapper.findConsumerByWechatOpenid(AppContext.getCurrentUserWechatOpenId()); consumerExist.setUpdatedBy(1L); consumerExist.setUpdatedAt(System.currentTimeMillis()); consumerExist.setGender(consumer.getGender()); consumerExist.setAvatarUrl(consumer.getAvatarUrl()); consumerExist.setWechatOpenid(consumer.getWechatOpenid()); consumerExist.setEmail(consumer.getEmail()); consumerExist.setNickname(consumer.getNickname()); consumerExist.setPhone(consumer.getPhone()); consumerExist.setUsername(consumer.getUsername()); consumerMapper.updateConsumer(consumerExist); } }
wx.login() 获取code,而后携带code发送请求到本身服务端,获取登陆信息。而后 wx.getUserInfo() 获取用户的基本信息,例如:昵称、头像等,上传本地服务器保存用户基本信息。
// 登陆 wx.login({ success: function(res) { if (res.code) { wx.request({ url: "http://localhost:8080/auth", data: { code: res.code }, method: "POST", header: { 'content-type': 'application/json', }, success: function (res) { console.log(res.data.access_token); var token = res.data.access_token; wx.getUserInfo({ success: res => { // 保存用户信息到服务端 wx.request({ url: "http://localhost:8080/updateConsumerInfo", data: res.userInfo, method: "POST", header: { 'Authorization': 'Bearer ' + token, 'content-type': 'application/json', }, success: function (res) { console.log("success"); }, fail: function (error) { console.log(error); } }) } }) }, fail: function (error) { console.log(error); } }) } else { console.log("error code " + res.errMsg); } } })
刷新微信小程序缓存,编译使发送请求
发送登陆请求,完成后获取到 access_token
发送获取用户信息请求
小程序请求本地服务器登陆接口
本地服务器请求微信服务器登陆接口
小程序请求本地服务器更新用户信息接口
redis保存会话信息
mysql数据库存储用户信息
微信小程序登陆JAVA后台
代码地址以下:
http://www.demodashi.com/demo/12736.html
注:本文著做权归做者,由demo大师代发,拒绝转载,转载须要做者受权