代码网 logo
AI

SpringSecurity和oauth2微信登陆

唐某2023-05-04 13:41:2246

对于SpringSecurity和oauth2的初学者来说,实现微信单点登录可能会比较困难,以下是参考代码:

1.在pom.xml文件中添加以下依赖:

复制代码
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
    <version>1.0.10.RELEASE</version>
</dependency>`

2.在application.properties文件中添加以下配置:

复制代码
#微信公众号的appid
wechat.appid=your wechat appid
#微信公众号的appsecret
wechat.secret=your wechat secret
#微信登录授权回调地址
wechat.redirect_uri=your redirect uri

3.创建WechatAuthenticationFilter过滤器,在该过滤器中实现微信登录的相关逻辑。逻辑如下:

  • 判断请求是否为微信授权回调。
  • 根据code值获取openid、access_token等信息。
  • 判断该openid是否已在系统中绑定过账号,如果绑定过,则直接返回jwt token,否则返回错误码。
复制代码
public class WechatAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final String WECHAT_AUTHENTICATE_URL = "/wechat/login/authenticate";

    private final Cache cache;

    @Autowired
    private WechatProperties wechatProperties;

    public WechatAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher,
                                      Cache cache) {
        super(requiresAuthenticationRequestMatcher);
        this.cache = cache;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException,
            IOException, ServletException {
        logger.info("微信登录鉴权");
        String code = request.getParameter("code");
        if (StringUtils.isBlank(code)) {
            throw new WechatAuthenticationException(ErrorCodeEnum.WECHAT_AUTH_CODE_REQUIRED.getErrorCode(),
                    ErrorCodeEnum.WECHAT_AUTH_CODE_REQUIRED.getErrorMsg());
        }

        WechatAuthUserInfo wechatAuthUserInfo = WechatAuthUtils.getUserInfoByCodeAndAccessToken(
                code, wechatProperties.getAppid(), wechatProperties.getSecret());

        // 检查是否已绑定用户
        String openid = wechatAuthUserInfo.getOpenid();
        UserAccountDao mainUserDao = BeanUtils.getBean(UserAccountDao.class);
        UserAccount userAccount = mainUserDao.getByOpenid(openid);
        if (userAccount == null) {
            throw new WechatAuthenticationException(
                    ErrorCodeEnum.WECHAT_AUTH_OPENID_NOT_BIND.getErrorCode(),
                    ErrorCodeEnum.WECHAT_AUTH_OPENID_NOT_BIND.getErrorMsg());
        }

        logger.info("微信登录鉴权成功");

        // 生成jwt token并返回
        JwtTokenProperties jwtTokenProperties = BeanUtils.getBean(JwtTokenProperties.class);
        String token = JwtUtils.createToken(userAccount, jwtTokenProperties.getExpireInMinute(),
                jwtTokenProperties.getSecret());
        response.setHeader("Access-Token", token);
        return new WechatAuthenticationToken(userAccount);
    }

    /**
     * 匹配url是否是微信授权回调
     * 
     * @param request
     *            请求
     * @return boolean
     */
    @Override
    protected boolean requiresAuthentication(HttpServletRequest request,
            HttpServletResponse response) {
        return StringUtils.endsWith(request.getRequestURI(), WECHAT_AUTHENTICATE_URL) && request
                .getMethod().equalsIgnoreCase("get");
    }
}

4.创建WechatAuthenticationProvider提供器,用于保存登录状态和返回JWT token。

复制代码
public class WechatAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private Cache cache;

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        WechatAuthenticationToken wechatAuthenticationToken = (WechatAuthenticationToken) authentication;
        UserAccount userAccount = wechatAuthenticationToken.getUserAccount();
        cache.put(userAccount.getUuid(), userAccount);
        return wechatAuthenticationToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return WechatAuthenticationToken.class.equals(authentication);
    }
}

5.创建WechatAuthenticationToken类和WechatAuthenticationException类。

复制代码
public class WechatAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = -2872688686791428001L;
    private final UserAccount userAccount;

    public WechatAuthenticationToken(UserAccount userAccount) {
        super(null);
        this.userAccount = userAccount;
        super.setAuthenticated(true);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.emptyList();
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return userAccount;
    }

    public UserAccount getUserAccount() {
        return userAccount;
    }
}
复制代码
public class WechatAuthenticationException extends AuthenticationException {

    private static final long serialVersionUID = 3045924610355703429L;
    private String errorCode;

    public WechatAuthenticationException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }
}

6.创建WebSecurityConfigurerAdapter类,使过滤器和提供器生效。

复制代码
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private Cache cache;

    @Autowired
    private WechatAuthenticationProvider wechatAuthenticationProvider;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private WechatProperties wechatProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() // 关闭csrf
                .authorizeRequests().antMatchers("/wechat/**").permitAll() // 微信登录授权
                .antMatchers("/**").authenticated() // 其他接口需要登陆
                .and().anonymous().disable().exceptionHandling()
                .authenticationEntryPoint(new RestAuthenticationEntryPoint())
                .and()
                .addFilterBefore(wechatAuthenticationFilter(),
                        UsernamePasswordAuthenticationFilter.class) // 微信授权登录过滤器
                .addFilterBefore(jwtAuthenticationFilter,
                        RequestCacheAwareFilter.class); // jwt验证过滤器

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**");
        super.configure(web);
    }

    @Bean
    public WechatAuthenticationFilter wechatAuthenticationFilter() {
        String wechatRedirectUri = wechatProperties.getRedirectUri();
        String wechatAuthorizeUrl = WechatAuthUtils.generateAuthorizeUrl(wechatProperties.getAppid(),
                URLEncoder.encode(wechatRedirectUri), WechatAuthScopeEnum.SNSAPI_USERINFO);
        return new WechatAuthenticationFilter(
                new AntPathRequestMatcher(WECHAT_AUTHENTICATE_URL),
                cache);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(wechatAuthenticationProvider)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

以上就是在SpringSecurity + oauth2中使用微信单点登录的详细代码。

广告