@Autowired
@Qualifier(“jwtTokenStore”)
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private B8AuthUserDetailService userDetailService;
@Autowired
private AuthenticationManager authenticationManagerBean;
@Autowired
private B8AuthTokenEnhancer b8AuthTokenEnhancer;
/**
-
@Description 第三方信息的存储
-
@MethodParameterTypes [org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer]
-
@MethodParameters [clients]
-
@MethodReturnType void
-
@Author zhiwei Liao
-
@Date 2021/8/17 14:57
**/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 第三方信息的存储 基于jdbc
clients.withClientDetails(clientDetailsService());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//配置JWT的内容增强器
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List delegates = new ArrayList<>();
delegates.add(b8AuthTokenEnhancer);
delegates.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(delegates);
//使用密码模式需要配置
endpoints.authenticationManager(authenticationManagerBean)
.reuseRefreshTokens(false) //refresh_token是否重复使用
.userDetailsService(userDetailService) //刷新令牌授权包含对用户信息的检查
.tokenStore(new JdbcTokenStore(dataSource)) //指定token存储策略是jwt,存储到mysql
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(enhancerChain) //配置tokenEnhancer
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求
}
@Bean
public JdbcTokenStore jdbcTokenStore(){
return new JdbcTokenStore(dataSource);
}
/**
-
授权服务器安全配置
-
@param security
-
@throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//第三方客户端校验token需要带入 clientId 和clientSecret来校验
security.checkTokenAccess(“isAuthenticated()”)
.tokenKeyAccess(“isAuthenticated()”);//来获取我们的tokenKey需要带入clientId,clientSecret
//允许表单认证
security.allowFormAuthenticationForClients();
}
@Bean
public ClientDetailsService clientDetailsService(){
return new JdbcClientDetailsService(dataSource);
}
}
AuthResourceServerConfig
package com.b8.auth.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
- 资源服务配置
*/
@Configuration
@EnableResourceServer
public class AuthResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
JwtTokenStoreConfig
package com.b8.auth.config;
import com.b8.auth.enhancer.B8AuthTokenEnhancer;
import com.b8.auth.properties.JwtCAProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import java.security.KeyPair;
@Configuration
@EnableConfigurationProperties(value = JwtCAProperties.class)
public class JwtTokenStoreConfig {
@Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public B8AuthTokenEnhancer b8AuthTokenEnhancer() {
return new B8AuthTokenEnhancer();
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new
JwtAccessTokenConverter();
//配置JWT使用的秘钥 非对称加密
accessTokenConverter.setKeyPair(keyPair());
return accessTokenConverter;
}
@Autowired
private JwtCAProperties jwtCAProperties;
@Bean
public KeyPair keyPair() {
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtCAProperties.getKeyPairName()), jwtCAProperties.getKeyPairSecret().toCharArray());
return keyStoreKeyFactory.getKeyPair(jwtCAProperties.getKeyPairAlias(), jwtCAProperties.getKeyPairStoreSecret().toCharArray());
}
}
RedisConfig
package com.b8.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/**
- @author zhiwei Liao
*/
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore(){
// access_token
return new RedisTokenStore(redisConnectionFactory);
}
}
WebSecurityConfig
package com.b8.auth.config;
import com.b8.auth.service.B8AuthUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
-
@Description 配置SpringSecurity,“将Spring Security与Spring Gateway一起使用时出现无法访问javax.servlet.Filter”错误”,
-
把Spring Gateway和Spring Security放在一起,因为我想保护我的网关。但是在实现了以下扩展WebSecurityConfigurerAdapter的类之后,
-
项目抛出java:无法访问javax.servlet.Filter
-
从Spring Cloud Gateway文档中:Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时。
-
它不能在传统的Servlet容器中工作,也不能在构建为WAR时工作。扩展WebSecurityConfigurerAdapter是为了基于servlet的应用程序
-
@Author zhiwei Liao
-
@Date 2021/8/17 15:44
**/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private B8AuthUserDetailService userDetailService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
-
@Description 密码模式
-
@MethodReturnType org.springframework.security.crypto.password.PasswordEncoder
-
@Author zhiwei Liao
-
@Date 2021/8/17 15:46
**/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().permitAll()
.and().authorizeRequests()
.antMatchers(“/oauth/**”).permitAll()//不拦截
.anyRequest()
.authenticated()
.and().logout().permitAll()//退出放行
.and().csrf().disable();
}
}
UserinfoDetails
package com.b8.auth.domain;
import com.common.entity.po.B8UserUserinfoEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
/**
- @author zhiwei Liao
*/
public class UserinfoDetails implements UserDetails {
private B8UserUserinfoEntity userUserinfo;
public UserinfoDetails(B8UserUserinfoEntity userUserinfo) {
this.userUserinfo = userUserinfo;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//返回当前用户的权限 BRAC user role authority
return Arrays.asList(new SimpleGrantedAuthority(“TEST”));
}
// 获取用户密码(凭证)
@Override
public String getPassword() {
return userUserinfo.getCredential();
}
// 获取用户名
@Override
public String getUsername() {
return userUserinfo.getNickNameId();
}
// 判断帐号是否已经过期
@Override
public boolean isAccountNonExpired() {
return true;
}
// 判断帐号是否已被锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
// 判断用户凭证是否已经过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 判断用户帐号是否已启用
@Override
public boolean isEnabled() {
return !userUserinfo.getUserStatus().equals(“FREEZE”);
}
public B8UserUserinfoEntity getUserUserinfo() {
return userUserinfo;
}
}
B8AuthTokenEnhancer
package com.b8.auth.enhancer;
import com.b8.auth.domain.UserinfoDetails;
import com.common.entity.po.B8UserUserinfoEntity;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
public class B8AuthTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
UserinfoDetails userinfoDetails = (UserinfoDetails) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
final Map<String, Object> retMap = new HashMap<>();
//todo 这里暴露userId到Jwt的令牌中,后期可以根据自己的业务需要 进行添加字段
additionalInfo.put(“userId”,userinfoDetails.getUserUserinfo().getId());
additionalInfo.put(“userName”,userinfoDetails.getUserUserinfo().getNickNameId());
additionalInfo.put(“nickName”,userinfoDetails.getUserUserinfo().getDisplayName());
additionalInfo.put(“loginType”,userinfoDetails.getUserUserinfo().getLoginType());
retMap.put(“additionalInfo”,additionalInfo);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(retMap);
return accessToken;
}
}
JwtCAProperties
package com.b8.auth.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
-
@Description 读取配置文件中的属性配置
-
@Author zhiwei Liao
-
@Date 2021/8/18 10:04
**/
@Data
@ConfigurationProperties(prefix = “b8auth.jwt”)
public class JwtCAProperties {
/**
- 证书名称
*/
private String keyPairName;
/**
- 证书别名
*/
private String keyPairAlias;
/**
- 证书私钥
*/
private String keyPairSecret;
/**
必看视频!获取2024年最新Java开发全套学习资料 备注Java
- 证书存储密钥
*/
private String keyPairStoreSecret;
}
UserServiceHystrix
package com.b8.auth.service.impl;
import com.b8.auth.api.ResultData;
import com.b8.auth.service.UserInfoFeignService;
import com.common.entity.po.B8UserUserinfoEntity;
import org.springframework.stereotype.Component;
/**
-
@author zhiwei Liao
-
@version 1.0
-
@Description
-
@Date 2021/8/17 15:25
*/
@Component
public class UserServiceHystrix implements UserInfoFeignService {
@Override
public ResultData getUserinfoById(String userId) {
return null;
}
@Override
public ResultData getUserByUsername(String username) {
return null;
}
}
B8AuthUserDetailService
package com.b8.auth.service;
import com.b8.auth.api.ResultData;
import com.b8.auth.domain.UserinfoDetails;
import com.common.entity.po.B8UserUserinfoEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
-
@author zhiwei Liao
-
@version 1.0
-
@Description
-
@Date 2021/8/17 15:02
*/
@Service
@Slf4j
public class B8AuthUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO 查数据库获取用户信息 rpc调用
// 加载用户信息
if (StringUtils.isEmpty(username)) {
log.warn(“用户登陆用户名为空:{}”, username);
throw new UsernameNotFoundException(“用户名不能为空”);
}
B8UserUserinfoEntity userUserinfo = getByUsername(username);
if (null == userUserinfo) {
log.warn(“根据用户名没有查询到对应的用户信息:{}”, username);
}
log.info(“根据用户名:{}获取用户登陆信息:{}”, username, userUserinfo);
// 用户信息的封装 implements UserDetails
UserinfoDetails memberDetails = new UserinfoDetails(userUserinfo);
return memberDetails;
}
@Autowired
private UserInfoFeignService userInfoFeignService;
public B8UserUserinfoEntity getByUsername(String username) {
// fegin获取用户信息
ResultData resultData = userInfoFeignService.getUserByUsername(username);
return resultData.getData();
}
}
UserInfoFeignService
package com.b8.auth.service;
import com.b8.auth.api.ResultData;
import com.b8.auth.service.impl.UserServiceHystrix;
import com.common.entity.po.B8UserUserinfoEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
-
@author zhiwei Liao
-
@version 1.0
-
@Description
-
@Date 2021/8/17 15:24
*/
@Component
@FeignClient(name = “user”, fallback = UserServiceHystrix.class, path = “/user”)
public interface UserInfoFeignService {
@GetMapping(“/getUserinfoById”)
ResultData getUserinfoById(@RequestParam(“userId”) String userId);
@GetMapping(“/getUserByUsername”)
ResultData getUserByUsername(@RequestParam(“username”) String username);
}
IErrorCode
package com.b8.auth.api;
/**
- 封装API的错误码
*/
public interface IErrorCode {
int getCode();
String getMessage();
}
ResultCode
package com.b8.auth.api;
/**
- 枚举了一些常用API操作码
*/
public enum ResultCode implements IErrorCode {
SUCCESS(200, “操作成功”),
FAILED(500, “操作失败”),
VALIDATE_FAILED(404, “参数检验失败”),
UNAUTHORIZED(401, “暂未登录或token已经过期”),
AUTHORIZATION_HEADER_IS_EMPTY(600,“请求头中的token为空”),
GET_TOKEN_KEY_ERROR(601,“远程获取TokenKey异常”),
GEN_PUBLIC_KEY_ERROR(602,“生成公钥异常”),
JWT_TOKEN_EXPIRE(603,“token校验异常”),
TOMANY_REQUEST_ERROR(429,“后端服务触发流控”),
BACKGROUD_DEGRADE_ERROR(604,“后端服务触发降级”),
BAD_GATEWAY(502,“网关服务异常”),
FORBIDDEN(403, “没有相关权限”);
private int code;
private String message;
private ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
ResultData
package com.b8.auth.api;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
public class ResultData implements Serializable {
/**
- 状态码
*/
public boolean status = true;
/**
- 状态码
*/
private Integer code = 200;
/**
- 接口返回信息
*/
private String msg;
/**
- 数据对象
*/
private T data;
/**
-
初始化一个新创建的 ResultData 对象
-
@param status 状态码
-
@param msg 返回内容
*/
public ResultData(Boolean status, String msg) {
this.status = status;
this.msg = msg;
}
/**
-
初始化一个新创建的 ResultData 对象
-
@param status 状态码
-
@param msg 返回内容
-
@param data 数据对象
*/
public ResultData(Boolean status, String msg, T data, Integer code) {
this.status = status;
this.msg = msg;
this.data = data;
this.code = code;
}
public ResultData(T data) {
this.data = data;
}
/**
-
返回成功消息
-
@param msg 返回内容
-
@param data 数据对象
-
@return 成功消息
*/
public static ResultData success(String msg, T data) {
return new ResultData(true, msg, data, 200);
}
/**
-
返回成功消息
-
@param msg 返回内容
-
@return 成功消息
*/
public static ResultData success(String msg) {
return ResultData.success(msg, null);
}
/**
-
返回成功消息
-
@return 成功消息
*/
public static ResultData success() {
return ResultData.success(null);
总结
我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。
面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!
/**
- 接口返回信息
*/
private String msg;
/**
- 数据对象
*/
private T data;
/**
-
初始化一个新创建的 ResultData 对象
-
@param status 状态码
-
@param msg 返回内容
*/
public ResultData(Boolean status, String msg) {
this.status = status;
this.msg = msg;
}
/**
-
初始化一个新创建的 ResultData 对象
-
@param status 状态码
-
@param msg 返回内容
-
@param data 数据对象
*/
public ResultData(Boolean status, String msg, T data, Integer code) {
this.status = status;
this.msg = msg;
this.data = data;
this.code = code;
}
public ResultData(T data) {
this.data = data;
}
/**
-
返回成功消息
-
@param msg 返回内容
-
@param data 数据对象
-
@return 成功消息
*/
public static ResultData success(String msg, T data) {
return new ResultData(true, msg, data, 200);
}
/**
-
返回成功消息
-
@param msg 返回内容
-
@return 成功消息
*/
public static ResultData success(String msg) {
return ResultData.success(msg, null);
}
/**
-
返回成功消息
-
@return 成功消息
*/
public static ResultData success() {
return ResultData.success(null);
总结
我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。
面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!
[外链图片转存中…(img-VT3efCv2-1716406720023)]