자바 에서 Shiro,JWT 를 바탕 으로 위 챗 애플 릿 로그 인 전체 예 및 실현 과정 을 실현 합 니 다.

애플 릿 공식 프로 세 스 도 는 다음 과 같 습 니 다.공식 주소:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html:

본 고 는 위 챗 애플 릿 사용자 정의 로그 인 을 위 한 완전한 예 입 니 다.기술 스 택 은 SpringBoot+Shiro+JWT+JPA+Redis 입 니 다.
이 예 에 관심 이 있 거나 언어 표현 이 지루 하 다 고 생각 되면 완전한 프로젝트 주 소 를 볼 수 있 습 니 다.https://github.com/EalenXie/shiro-jwt-applet
주요 구현:애플 릿 의 사용자 정의 로그 인 을 실현 하고 사용자 정의 로그 인 상태 token 을 애플 릿 에 로그 인 증빙 으로 되 돌려 줍 니 다.사용자 의 정 보 는 데이터베이스 에 저장 되 어 있 으 며,로그 인 상태 token 캐 시 는 redis 에 있 습 니 다.
효 과 는 다음 과 같 습 니 다:
1.우선 우리 애플 릿 에서 wx.login()을 호출 하여 임시 증빙 코드 를 가 져 옵 니 다.

2.이 코드 를 사용 하여 애플 릿 로그 인 을 통 해 사용자 정의 로그 인 상태 token 을 가 져 오고 postman 으로 테스트 합 니 다.

3.인증 이 필요 한 인 터 페 이 스 를 호출 하고 이 token 을 가지 고 감 권 을 하여 되 돌아 오 는 정 보 를 얻 습 니 다.  :

전방 고에너지,본 사례 의 코드 설명 이 비교적 많 고 다음은 주요 한 구축 절차 이다.
1.먼저 Maven 프로젝트 shiro-jwt-applet,pom 의존,주로 shiro 와 jwt 의 의존,그리고 SpringBoot 의 기초 의존.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>name.ealen</groupId>
 <artifactId>shiro-jwt-applet</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 <name>shiro-wx-jwt</name>
 <description>Demo project for Spring Boot</description>
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.6.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <java.version>1.8</java.version>
 </properties>
 <dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
 </dependency>
 <dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
 </dependency>
 <dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.4.1</version>
 </dependency>
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.47</version>
 </dependency>
 </dependencies>
 <build>
 <plugins>
  <plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  </plugin>
 </plugins>
 </build>
</project>
2.애플 리 케 이 션.yml 을 설정 합 니 다.주로 애플 리 케 이 션 appid 와 secret,그리고 데이터베이스 와 redis 를 설정 합 니 다.

##          
spring:
 application:
 name: shiro-jwt-applet
 jpa:
 hibernate:
 ddl-auto: create #                  
# datasource    
 datasource:
 url: jdbc:mysql://localhost:3306/yourdatabase
 username: yourname
 password: yourpass
 driver-class-name: com.mysql.jdbc.Driver
# redis          
 redis:
 database: 0
 host: localhost
 port: 6379
#         appid /appsecret
wx:
 applet:
 appid: yourappid
 appsecret: yourappsecret

3.우리 가 저장 한 위 챗 애플 릿 로그 인 을 정의 하 는 실체 정보 WxAccount  : 

package name.ealen.domain.entity;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
 * Created by EalenXie on 2018/11/26 10:26.
 *                 ,            
 */
@Entity
@Table
public class WxAccount {
 @Id
 @GeneratedValue
 private Integer id;
 private String wxOpenid;
 private String sessionKey;
 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
 private Date lastTime;
 /**
 *   getter/setter
 */
}

데이터베이스 WxAccountRepository 에 간단 한 dao 접근:

package name.ealen.domain.repository;
import name.ealen.domain.entity.WxAccount;
import org.springframework.data.jpa.repository.JpaRepository;
/**
 * Created by EalenXie on 2018/11/26 10:32.
 */
public interface WxAccountRepository extends JpaRepository<WxAccount, Integer> {
 /**
 *   OpenId      
 */
 WxAccount findByWxOpenid(String wxOpenId);
}

4.저희 가 사용 하 는 서비스 설명 Wx AppletService 를 정의 합 니 다.

package name.ealen.application;
import name.ealen.interfaces.dto.Token;
/**
 * Created by EalenXie on 2018/11/26 10:40.
 *                
 */
public interface WxAppletService {
 /**
 *          ,             ,          
 * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
 * 1 .            code。
 * 2 .     code2session    openid session_key
 * 3 .   openid session_key      (Token)
 * 4 .         (Token)     。
 * 5 .                 api,  header Authorization     token  
 *
 * @param code         wx.login     code,       code2session  
 * @return Token             token   JWT  
 */
 public Token wxUserLogin(String code);
}

 위 챗 애플 릿 token 대상 에 게 Token 설명 을 되 돌려 줍 니 다:

package name.ealen.interfaces.dto;
/**
 * Created by EalenXie on 2018/11/26 18:49.
 * DTO    token  
 */
public class Token {
 private String token;
 public Token(String token) {
 this.token = token;
 }
 /**
 *   getter/setter
 */
}

5.필요 한 기본 구성 요소,RestTemplate,Redis 설정:

package name.ealen.infrastructure.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
 * Created by EalenXie on 2018-03-23 07:37
 * RestTemplate    
 */
@Configuration
public class RestTemplateConfig {
 @Bean
 public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
 return new RestTemplate(factory);
 }
 @Bean
 public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
 SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
 factory.setReadTimeout(1000 * 60);      //          60 
 factory.setConnectTimeout(1000 * 10);     //         10 
 return factory;
 }
}

Redis 설정.이 예 는 Springboot 2.0 의 쓰기 입 니 다.

package name.ealen.infrastructure.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
 * Created by EalenXie on 2018-03-23 07:37
 * Redis    
 */
@Configuration
@EnableCaching
public class RedisConfig {

 @Bean
 public CacheManager cacheManager(RedisConnectionFactory factory) {
 return RedisCacheManager.create(factory);
 }
}

6.JWT 의 핵심 필터 설정.Shiro 의 Basic Http AuthenticationFilter 를 계승 하여 인증 권 의 여과 방법 을 다시 썼 습 니 다.

package name.ealen.infrastructure.config.jwt;
import name.ealen.domain.vo.JwtToken;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Created by EalenXie on 2018/11/26 10:26.
 * JWT       
 *           Filter,         BasicHttpAuthenticationFilter,         。
 *      preHandle->isAccessAllowed->isLoginAttempt->executeLogin
 */
public class JwtFilter extends BasicHttpAuthenticationFilter {

 /**
 *                   
 *   header      Authorization    
 */
 @Override
 protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
 String auth = getAuthzHeader(request);
 return auth != null && !auth.equals("");

 }
 /**
 *        ,    
 */
 @Override
 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
 if (isLoginAttempt(request, response)) {
  JwtToken token = new JwtToken(getAuthzHeader(request));
  getSubject(request, response).login(token);
 }
 return true;
 }
 /**
 *       
 */
 @Override
 protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 HttpServletResponse httpServletResponse = (HttpServletResponse) response;
 httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
 httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
 httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
 //           option  ,     option          
 if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
  httpServletResponse.setStatus(HttpStatus.OK.value());
  return false;
 }
 return super.preHandle(request, response);
 }
}

JWT 의 핵심 설정(Token 의 암호 화 생 성,JWT 연장,복호화 검증 포함):

package name.ealen.infrastructure.config.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import name.ealen.domain.entity.WxAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
 * Created by EalenXie on 2018/11/22 17:16.
 */
@Component
public class JwtConfig {
 /**
 * JWT             
 */
 private static final String SECRET_KEY = "5371f568a45e5ab1f442c38e0932aef24447139b";
 /**
 * JWT                     7200  ,       
 */
 private static long expire_time = 7200;
 @Autowired
 private StringRedisTemplate redisTemplate;
 /**
 *              token
 *   :    token     redis ,       
 * redis          jwt token         
 *
 * @param wxAccount       
 * @return    jwt token
 */
 public String createTokenByWxAccount(WxAccount wxAccount) {
 String jwtId = UUID.randomUUID().toString();   //JWT   ID,     key
 //1 .           token
 Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
 String token = JWT.create()
  .withClaim("wxOpenId", wxAccount.getWxOpenid())
  .withClaim("sessionKey", wxAccount.getSessionKey())
  .withClaim("jwt-id", jwtId)
  .withExpiresAt(new Date(System.currentTimeMillis() + expire_time*1000)) //JWT            
  .sign(algorithm);
 //2 . Redis  JWT,   :   JWT      
 redisTemplate.opsForValue().set("JWT-SESSION-" + jwtId, token, expire_time, TimeUnit.SECONDS);
 return token;
 }
 /**
 *   token    
 * 1 .   token  ,   jwt-id ,   redis    redisToken,      
 * 2 .     redisToken    ,               token  
 *
 * @param token   
 * @return         
 */
 public boolean verifyToken(String token) {
 try {
  //1 .   token  ,   jwt-id ,   redis    redisToken,      
  String redisToken = redisTemplate.opsForValue().get("JWT-SESSION-" + getJwtIdByToken(token));
  if (!redisToken.equals(token)) return false;
  //2 .        JWTVerifier
  Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
  JWTVerifier verifier = JWT.require(algorithm)
   .withClaim("wxOpenId", getWxOpenIdByToken(redisToken))
   .withClaim("sessionKey", getSessionKeyByToken(redisToken))
   .withClaim("jwt-id", getJwtIdByToken(redisToken))
   .acceptExpiresAt(System.currentTimeMillis() + expire_time*1000 ) //JWT          
   .build();
  //3 .   token
  verifier.verify(redisToken);
  //4 . Redis  JWT  
  redisTemplate.opsForValue().set("JWT-SESSION-" + getJwtIdByToken(token), redisToken, expire_time, TimeUnit.SECONDS);
  return true;
 } catch (Exception e) { //              
  return false;
 }
 }
 /**
 *   Token  wxOpenId(     :   token   ,       wxOpenId,  )
 */
 public String getWxOpenIdByToken(String token) throws JWTDecodeException {
 return JWT.decode(token).getClaim("wxOpenId").asString();
 }
 /**
 *   Token  sessionKey
 */
 public String getSessionKeyByToken(String token) throws JWTDecodeException {
 return JWT.decode(token).getClaim("sessionKey").asString();
 }
 /**
 *   Token   jwt-id
 */
 private String getJwtIdByToken(String token) throws JWTDecodeException {
 return JWT.decode(token).getClaim("jwt-id").asString();
 }
}

7.Shiro 의 Realm 설정 을 사용자 정의 합 니 다.Realm 은 사용자 정의 로그 인 및 권한 수 여 를 위 한 논리 적 설정 입 니 다.

package name.ealen.infrastructure.config.shiro;
import name.ealen.domain.vo.JwtToken;
import name.ealen.infrastructure.config.jwt.JwtConfig;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
 * Created by EalenXie on 2018/11/26 12:12.
 * Realm          allRealm()       realm
 */
@Component
public class ShiroRealmConfig {
 @Resource
 private JwtConfig jwtConfig;
 /**
 *         realm,    ,       realm   
 */
 public List<Realm> allRealm() {
 List<Realm> realmList = new LinkedList<>();
 AuthorizingRealm jwtRealm = jwtRealm();
 realmList.add(jwtRealm);
 return Collections.unmodifiableList(realmList);
 }
 /**
 *     JWT  Realm
 *    Realm   supports()       JWT          
 */
 private AuthorizingRealm jwtRealm() {
 AuthorizingRealm jwtRealm = new AuthorizingRealm() {
  /**
  *      :        ,  Shiro   
  *       JWTToken     Shiro   token,                ,             
  */
  @Override
  public boolean supports(AuthenticationToken token) {
  return token instanceof JwtToken;
  }
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  return new SimpleAuthorizationInfo();
  }
  /**
  *      token  
  */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
  String jwtToken = (String) token.getCredentials();
  String wxOpenId = jwtConfig.getWxOpenIdByToken(jwtToken);
  String sessionKey = jwtConfig.getSessionKeyByToken(jwtToken);
  if (wxOpenId == null || wxOpenId.equals(""))
   throw new AuthenticationException("user account not exits , please check your token");
  if (sessionKey == null || sessionKey.equals(""))
   throw new AuthenticationException("sessionKey is invalid , please check your token");
  if (!jwtConfig.verifyToken(jwtToken))
   throw new AuthenticationException("token is invalid , please check your token");
  return new SimpleAuthenticationInfo(token, token, getName());
  }
 };
 jwtRealm.setCredentialsMatcher(credentialsMatcher());
 return jwtRealm;
 }
 /**
 *      :      ,      JWT  ,          ,       true(       ,     false,        )
 */
 private CredentialsMatcher credentialsMatcher() {
 return (token, info) -> true;
 }
}

Shiro 의 핵심 설정,Realm 설정 포함:

package name.ealen.infrastructure.config.shiro;
import name.ealen.infrastructure.config.jwt.JwtFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by EalenXie on 2018/11/22 18:28.
 */
@Configuration
public class ShirConfig {
 /**
 * SecurityManager,     ,                  ;
 *       Subject,  Subject    SecurityManager, Subject          SecurityManager
 * DefaultWebSecurityManager :
 *       DefaultSubjectDAO(       DefaultSessionStorageEvaluator)
 *      DefaultWebSubjectFactory
 *      ModularRealmAuthenticator
 */
 @Bean
 public DefaultWebSecurityManager securityManager(ShiroRealmConfig shiroRealmConfig) {
 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 securityManager.setRealms(shiroRealmConfig.allRealm()); //  realm
 DefaultSubjectDAO subjectDAO = (DefaultSubjectDAO) securityManager.getSubjectDAO();
 //     session
 DefaultSessionStorageEvaluator evaluator = (DefaultSessionStorageEvaluator) subjectDAO.getSessionStorageEvaluator();
 evaluator.setSessionStorageEnabled(Boolean.FALSE);
 subjectDAO.setSessionStorageEvaluator(evaluator);
 return securityManager;
 }
 /**
 *   Shiro     
 */
 @Bean
 public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
 ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
 Map<String, Filter> filterMap = new HashMap<>();
 filterMap.put("jwt", new JwtFilter());
 factoryBean.setFilters(filterMap);
 factoryBean.setSecurityManager(securityManager);
 Map<String, String> filterRuleMap = new HashMap<>();
 //    api         
 filterRuleMap.put("/api/wx/user/login/**", "anon");
 filterRuleMap.put("/api/response/**", "anon");
 //       JWT Filter
 filterRuleMap.put("/**", "jwt");
 factoryBean.setFilterChainDefinitionMap(filterRuleMap);
 return factoryBean;
 }
 /**
 *       
 */
 @Bean
 @DependsOn("lifecycleBeanPostProcessor")
 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
 DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
 defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); //     cglib,                  
 return defaultAdvisorAutoProxyCreator;
 }
 /**
 *       
 */
 @Bean
 public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 return new LifecycleBeanPostProcessor();
 }

 /**
 *       
 */
 @Bean
 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
 return authorizationAttributeSourceAdvisor;
 }
}

Shiro 인증 에 사용 할 JwtToken 대상:

package name.ealen.domain.vo;
import org.apache.shiro.authc.AuthenticationToken;
/**
 * Created by EalenXie on 2018/11/22 18:21.
 *     token vo ,   AuthenticationToken
 */
public class JwtToken implements AuthenticationToken {
 private String token;
 public JwtToken(String token) {
 this.token = token;
 }
 @Override
 public Object getPrincipal() {
 return token;
 }
 @Override
 public Object getCredentials() {
 return token;
 }
 public String getToken() {
 return token;
 }
 public void setToken(String token) {
 this.token = token;
 }
}

8.실체의 행위 와 업무 논 리 를 실현 합 니 다.이 예 는 주로 위 챗 인터페이스 code2session 을 호출 하고 token 으로 되 돌아 가 는 것 입 니 다.   

package name.ealen.domain.service;
import name.ealen.application.WxAppletService;
import name.ealen.domain.entity.WxAccount;
import name.ealen.domain.repository.WxAccountRepository;
import name.ealen.domain.vo.Code2SessionResponse;
import name.ealen.infrastructure.config.jwt.JwtConfig;
import name.ealen.infrastructure.util.HttpUtil;
import name.ealen.infrastructure.util.JSONUtil;
import name.ealen.interfaces.dto.Token;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
import java.util.Date;
/**
 * Created by EalenXie on 2018/11/26 10:50.
 *        
 */
@Service
public class WxAccountService implements WxAppletService {
 @Resource
 private RestTemplate restTemplate;
 @Value("${wx.applet.appid}")
 private String appid;
 @Value("${wx.applet.appsecret}")
 private String appSecret;
 @Resource
 private WxAccountRepository wxAccountRepository;
 @Resource
 private JwtConfig jwtConfig;
 /**
 *     code2session            
 *      : https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
 */
 private String code2Session(String jsCode) {
 String code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
 MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
 params.add("appid", appid);
 params.add("secret", appSecret);
 params.add("js_code", jsCode);
 params.add("grant_type", "authorization_code");
 URI code2Session = HttpUtil.getURIwithParams(code2SessionUrl, params);
 return restTemplate.exchange(code2Session, HttpMethod.GET, new HttpEntity<String>(new HttpHeaders()), String.class).getBody();
 }
 /**
 *          ,             ,          
 * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
 *
 * @param code         wx.login     code,       code2session  
 * @return             token   JWT  
 */
 @Override
 public Token wxUserLogin(String code) {
 //1 . code2session  JSON  
 String resultJson = code2Session(code);
 //2 .     
 Code2SessionResponse response = JSONUtil.jsonString2Object(resultJson, Code2SessionResponse.class);
 if (!response.getErrcode().equals("0"))
  throw new AuthenticationException("code2session   : " + response.getErrmsg());
 else {
  //3 .                 
  WxAccount wxAccount = wxAccountRepository.findByWxOpenid(response.getOpenid());
  if (wxAccount == null) {
  wxAccount = new WxAccount();
  wxAccount.setWxOpenid(response.getOpenid()); //        
  }
  //4 .   sessionKey      
  wxAccount.setSessionKey(response.getSession_key());
  wxAccount.setLastTime(new Date());
  wxAccountRepository.save(wxAccount);
  //5 . JWT          Token
  String token = jwtConfig.createTokenByWxAccount(wxAccount);
  return new Token(token);
 }
 }
}

애플 릿 code2session 인터페이스의 반환 VO 대상 Code2Session Response:

package name.ealen.domain.vo;
/**
 *       Code2Session         
 *            API   : https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
 */
public class Code2SessionResponse {
 private String openid;
 private String session_key;
 private String unionid;
 private String errcode = "0";
 private String errmsg;
 private int expires_in;
 /**
 *   getter/setter
 */
}

9.  Google 의 인터페이스 정 보 를 정의 합 니 다.이 예 는 로그 인 으로 token 을 가 져 오 는 api 와 인증 이 필요 한 테스트 api 를 포함 합 니 다.

package name.ealen.interfaces.facade;
import name.ealen.application.WxAppletService;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by EalenXie on 2018/11/26 10:44.
 *         API
 */
@RestController
public class WxAppletController {
 @Resource
 private WxAppletService wxAppletService;
 /**
 *           api
 *                token
 */
 @PostMapping("/api/wx/user/login")
 public ResponseEntity wxAppletLoginApi(@RequestBody Map<String, String> request) {
 if (!request.containsKey("code") || request.get("code") == null || request.get("code").equals("")) {
  Map<String, String> result = new HashMap<>();
  result.put("msg", "    code code   ");
  return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
 } else {
  return new ResponseEntity<>(wxAppletService.wxUserLogin(request.get("code")), HttpStatus.OK);
 }
 }
 /**
 *              @RequiresAuthentication   ,         header           authorization
 */
 @RequiresAuthentication
 @PostMapping("/sayHello")
 public ResponseEntity sayHello() {
 Map<String, String> result = new HashMap<>();
 result.put("words", "hello World");
 return new ResponseEntity<>(result, HttpStatus.OK);
 }
}
10.주 클래스 를 실행 하고 데이터베이스 와 redis 의 연결 을 검사 하 며 테스트 를 진행 합 니 다. 

package name.ealen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Created by EalenXie on 2018/11/26 10:25.
 */
@SpringBootApplication
public class ShiroJwtAppletApplication {
 public static void main(String[] args) {
 SpringApplication.run(ShiroJwtAppletApplication.class, args);
 }
총결산
위 에서 말 한 것 은 편집장 이 여러분 에 게 소개 한 자바 에서 Shiro,JWT 를 바탕 으로 위 챗 애플 릿 로그 인 을 실현 하 는 완전한 예 와 실현 과정 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.편집장 은 신속하게 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기