能正常根据SQL查询到用户即可
@Configuration
public class JsonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
wechat:
appid: xxx #前往微信公众平台查看自己的微信小程序appid
secret: xxx #前往微信公众平台查看自己的微信小程序secret
login-url: https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type={grant_type}
/**
* 微信配置实体类
*/
@Configuration
@ConfigurationProperties(prefix = "wechat")
@Data
public class WechatConfig {
private String appid;
private String secret;
private String loginUrl;
}
/**
* 微信请求响应类
*/
@Data
public class WechatLoginInfo {
private String session_key;
private String openid;
private String errcode;
private String errmsg;
private String rid;
}
@Getter
public class Result<T> {
private final Integer code;
private final String message;
private final T data;
private Result(HttpStatus httpStatus, T data) {
this.code = httpStatus.value();
this.message = httpStatus.getReasonPhrase();
this.data = data;
}
private Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> of(Integer code, String message, T data) {
return new Result<>(code, message, data);
}
/**
* 成功
*/
public static <T> Result<T> success(T data) {
return new Result<>(HttpStatus.OK, data);
}
/**
* 成功
*/
public static <T> Result<T> success() {
return new Result<>(HttpStatus.OK, null);
}
/**
* 失败
*/
public static Result<String> error() {
return new Result<>(HttpStatus.INTERNAL_SERVER_ERROR, null);
}
/**
* 失败
*/
public static Result<String> error(Throwable throwable) {
return new Result<>(HttpStatus.INTERNAL_SERVER_ERROR, throwable.getMessage());
}
/**
* 未登录
*/
public static Result<String> unauthorized() {
return new Result<>(HttpStatus.UNAUTHORIZED, null);
}
/**
* 未登录
*/
public static Result<String> forbidden() {
return new Result<>(HttpStatus.FORBIDDEN, null);
}
}
/**
* 认证失败自定义响应
*/
@Slf4j
@Component
public class JsonAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final ObjectMapper objectMapper;
public JsonAuthenticationEntryPoint(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.error("认证失败", authException);
System.out.println(request.getRequestURI());
response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write(objectMapper.writeValueAsString(Result.unauthorized()));
}
}
/**
* 鉴权失败自定义响应
*/
@Component
@Slf4j
public class JsonAccessDeniedHandler implements AccessDeniedHandler {
private final ObjectMapper objectMapper;
public JsonAccessDeniedHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("权限不足", accessDeniedException);
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write(objectMapper.writeValueAsString(Result.forbidden()));
}
}
/**
* 登录成功自定义处理(过滤器级别)
*/
@Slf4j
@Component
public class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final ObjectMapper objectMapper;
public JsonAuthenticationSuccessHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpStatus.OK.value());
response.getWriter().write(objectMapper.writeValueAsString(Result.success()));
}
}
/**
* 登录失败自定义处理(过滤器级别)
*/
@Component
@Slf4j
public class JsonAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final ObjectMapper objectMapper;
public JsonAuthenticationFailureHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write(objectMapper.writeValueAsString(Result.unauthorized()));
}
}
public interface UserService extends UserDetailsService {
Optional<User> findByOpenId(String openId);
User register(String openId);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public Optional<User> findByOpenId(String openId) {
return userRepository.findByOpenId(openId);
}
@Override
public User register(String openId) {
User user = User.registerByWechat(openId);
return userRepository.saveAndFlush(user);
}
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUsername(username);
return user.orElse(new User());
}
}
不要使用@component,在security config中初始化,否则会导致循环依赖
@Slf4j
public class WeChatAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final HashMap<String, Object> uriVariables = new HashMap<>();
private static final String CODE_PARAMETER = "code";
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/wechat/login", "GET");
private RestTemplate restTemplate;
private ObjectMapper objectMapper;
private WechatConfig wechatConfig;
public WeChatAuthenticationFilter() {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public void setWechatConfig(WechatConfig wechatConfig) {
this.wechatConfig = wechatConfig;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("GET")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
//校验code
String code = this.obtainCode(request);
//发送登录请求
WechatLoginInfo wechatLoginInfo = wechatLogin(code);
//创建未认证token
WechatAuthenticationToken authRequest = WechatAuthenticationToken.unauthenticated(wechatLoginInfo.getOpenid());
//验证token
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
/**
* 微信登录返回用户openid
*/
@SneakyThrows
private WechatLoginInfo wechatLogin(String code) {
this.fillVariables(code);
String response = restTemplate.getForObject(wechatConfig.getLoginUrl(), String.class, uriVariables);
log.info("response = {}", response);
WechatLoginInfo wechatLoginInfo = objectMapper.readValue(response, WechatLoginInfo.class);
return Optional.of(wechatLoginInfo).filter(obj -> obj.getErrcode() == null).orElseThrow(() -> new AuthenticationServiceException("微信登录异常"));
}
/**
* 封装请求参数
*/
private void fillVariables(String code) {
uriVariables.put("appid", wechatConfig.getAppid());
uriVariables.put("secret", wechatConfig.getSecret());
uriVariables.put("grant_type", "authorization_code");
uriVariables.put("js_code", code);
}
/**
* 校验授权码code
*/
private String obtainCode(HttpServletRequest request) {
String code = request.getParameter(CODE_PARAMETER);
return Optional.of(code).orElseThrow(() -> new AuthenticationServiceException("code is empty"));
}
@Autowired
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
protected void setDetails(HttpServletRequest request, WechatAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
}
@Slf4j
@Component
public class WechatAuthenticationProvider implements AuthenticationProvider {
private final UserService userService;
@Autowired
public WechatAuthenticationProvider(UserService userService) {
this.userService = userService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//获取openId
String openId = authentication.getName();
//根据openId查询用户
Optional<User> userOptional = userService.findByOpenId(openId);
//用户存在则获取,不存在则注册
User user = userOptional.orElseGet(() -> userService.register(openId));
return WechatAuthenticationToken.authenticated(openId, user.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return WechatAuthenticationToken.class.isAssignableFrom(authentication);
}
}
public class WechatAuthenticationToken extends AbstractAuthenticationToken {
private final String openId;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
public WechatAuthenticationToken(String openId) {
super(null);
this.openId = openId;
setAuthenticated(false);
}
public WechatAuthenticationToken(String openId, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.openId = openId;
setAuthenticated(true);
}
public static WechatAuthenticationToken unauthenticated(String openId) {
return new WechatAuthenticationToken(openId);
}
public static WechatAuthenticationToken authenticated(String openId, Collection<? extends GrantedAuthority> authorities) {
return new WechatAuthenticationToken(openId, authorities);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return openId;
}
}
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
private final UserService userService;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private final WechatConfig wechatConfig;
private final JsonAuthenticationEntryPoint jsonAuthenticationEntryPoint;
private final JsonAccessDeniedHandler jsonAccessDeniedHandler;
private final JsonAuthenticationSuccessHandler jsonAuthenticationSuccessHandler;
private final JsonAuthenticationFailureHandler jsonAuthenticationFailureHandler;
private final WechatAuthenticationProvider wechatAuthenticationProvider;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public SecurityConfiguration(UserService userService,
RestTemplate restTemplate,
ObjectMapper objectMapper,
WechatConfig wechatConfig,
JsonAuthenticationEntryPoint jsonAuthenticationEntryPoint,
JsonAccessDeniedHandler jsonAccessDeniedHandler,
JsonAuthenticationSuccessHandler jsonAuthenticationSuccessHandler,
JsonAuthenticationFailureHandler jsonAuthenticationFailureHandler,
WechatAuthenticationProvider wechatAuthenticationProvider,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
this.wechatConfig = wechatConfig;
this.jsonAuthenticationEntryPoint = jsonAuthenticationEntryPoint;
this.jsonAccessDeniedHandler = jsonAccessDeniedHandler;
this.jsonAuthenticationSuccessHandler = jsonAuthenticationSuccessHandler;
this.jsonAuthenticationFailureHandler = jsonAuthenticationFailureHandler;
this.wechatAuthenticationProvider = wechatAuthenticationProvider;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin(form -> form
.successHandler(jsonAuthenticationSuccessHandler)
.failureHandler(jsonAuthenticationFailureHandler)
)
.authorizeHttpRequests(request -> request.anyRequest().authenticated())
.authenticationManager(authenticationManager())
.exceptionHandling(exception -> exception
.authenticationEntryPoint(jsonAuthenticationEntryPoint)
.accessDeniedHandler(jsonAccessDeniedHandler)
)
.addFilterBefore(weChatAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(wechatAuthenticationProvider, daoAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
return daoAuthenticationProvider;
}
@Bean
public WeChatAuthenticationFilter weChatAuthenticationFilter() {
WeChatAuthenticationFilter weChatAuthenticationFilter = new WeChatAuthenticationFilter();
weChatAuthenticationFilter.setObjectMapper(objectMapper);
weChatAuthenticationFilter.setRestTemplate(restTemplate);
weChatAuthenticationFilter.setWechatConfig(wechatConfig);
weChatAuthenticationFilter.setAuthenticationSuccessHandler(jsonAuthenticationSuccessHandler);
weChatAuthenticationFilter.setAuthenticationFailureHandler(jsonAuthenticationFailureHandler);
return weChatAuthenticationFilter;
}
}
@Getter
@Setter
@Entity
public class User implements Serializable, UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String openId;
private String username;
private String password;
private String name;
private Integer gender;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private Boolean enable;
@ManyToOne(cascade={CascadeType.REMOVE,CascadeType.REFRESH})
@JoinColumn(name = "tenant_id")
@JsonBackReference
private Tenant tenant;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role",joinColumns =@JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))
@JsonBackReference
private List<Role> roleList;
private static final long serialVersionUID = 1L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roleList;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enable;
}
public static User registerByWechat(String openId){
User user = new User();
user.setOpenId(openId);
user.setName("游客");
user.setEnable(true);
user.setBirthday(new Date());
return user;
}
}
@Entity
@Getter
@Setter
public class Role implements Serializable, GrantedAuthority {
@Id
private Long id;
private String name;
@ManyToMany(mappedBy = "roleList")
@JsonBackReference
private List<User> userList;
private static final long serialVersionUID = 1L;
@Override
public String getAuthority() {
return name;
}
}