本模块是由 SpringBoot + Spring Security + Spring Security OAuth2 + Mybatis Plus + Mysql 组成的一套开发认证授权框架。该套框架基于约定大于配置的原则实现了大部分项目开发中需要用到的功能模块,比如接口权限控制 JWT Token、App 认证、MybatisPlus 集成与配置、Swagger 成与配置、跨域配置、全局异常处理、API 接口实现统一格式返回等等。
本模块基于 Jdk 1.8、SpringBoot2 开发,同时遵循《阿里巴巴 Java 开发手册》、API 接口遵循 Restful 风格。
建议配置
大家都知道怎么使用,不赘述。
继承GlobalCorsConfig
@Configuration
@EnableWebMvc
public class CorsConfig extends GlobalCorsConfig {
}
继承GlobalExceptionHandler
@ControllerAdvice
@Component
public class ExceptionHandler extends GlobalExceptionHandler {
}
继承GlobalMybatisConfig
@Configuration
public class MybatisPlusConfig extends GlobalMybatisConfig {
}
继承GloablSwaggerConfig
@Configuration
@EnableSwagger2
public class SwaggerConfig extends GloablSwaggerConfig {
}
Swagger 通常只在本地开发环境或内网测试环境开启,生产环境中 Swagger 一般都是需要关闭的。因此我们提供了一个抽象方法 boolean swaggerEnable()需要你来指定 Swagger 是否开启,通常这个开关配置在配置文件中,开发环境设置为 true,生产设置成 false,如下:
@Configuration
@EnableSwagger2
public class SwaggerConfig extends GloablSwaggerConfig {
@Value("${swagger.enable}")
private boolean enable;
@Override
public boolean swaggerEnable() {
return enable;
}
}
有时候 API 会分为多个模块,因此需要对 API 进行分组显示,此时可以重写configureSwaggerApiInfo()
方法,返回多个你需要的 ApiInfo,例如:
@Override
protected List<SwaggerApiInfo> configureSwaggerApiInfo() {
List<SwaggerApiInfo> swaggerApiInfos = new ArrayList<>();
SwaggerApiInfo xxxApiInfo1 = new SwaggerApiInfo("xxx接口文档","com.xxx.xxx.controller","V1.0");
swaggerApiInfos.add(xxxApiInfo1);
SwaggerApiInfo xxxApiInfo2 = new SwaggerApiInfo("xxx接口文档","com.xxx.xxx.controller","V1.0");
swaggerApiInfos.add(xxxApiInfo2);
return swaggerApiInfos;
}
暂时最多支持配置 10 个 Docket,正常情况下 10 个已经足够了,如果需要,框架还能提供更多的配置数量。
密码加密方式默认用的是BCryptPasswordEncoder
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
IUserAdminService userAdminServiceImpl;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
String password = bCryptPasswordEncoder.encode(userAdminServiceImpl.getUserPassword(userName));
return new User(userName, password, getAuthority());
}
private List getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class WebSecurityConfigurer extends GlobalWebSecurityConfigurer {
}
@Configuration
public class Oauth2AuthentizationConfig extends OAuth2AuthorizationServerConfig {
}
@Configuration
public class Oauth2ResoucesConfig extends OAuth2ResourceServerConfig {
}
为了方便开发,一般我们在本地开发环境中会关闭接口权限校验,重写资源服务配置OAuth2ResourceServerConfig
下的customCloseAuthorityEvironment()
方法,你可以指定某种环境下关闭接口权限校验,如下:
@Configuration
public class Oauth2ResoucesConfig extends OAuth2ResourceServerConfig {
@Value("${spring.profiles.active}")
private String currentRunEnvironment;
/**
* 指定某种运行环境下关闭权限校验;为了方便开发,一般我们的dev环境会关闭接口权限校验
* @return
*/
@Override
public CloseAuthorityEvironment customCloseAuthorityEvironment(){
return new CloseAuthorityEvironment(currentRunEnvironment,"dev");
}
}
如果你需要指定某些接口要放行,你可以重写重写资源服务配置customConfigure(HttpSecurity http)
,通过 HttpSecurity 设置放行接口,然后返回设置后的 HttpSecurity
/**
* @author nbbjack
* @create 2020-03-20 13:15
*/
@Configuration
public class Oauth2ResoucesConfig extends OAuth2ResourceServerConfig {
@Value("${spring.profiles.active}")
private String currentRunEnvironment;
/**
* 用户自定义配置,子类可覆盖自定义实现
* @param http
* @throws Exception
*/
@Override
protected HttpSecurity customConfigure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/verify-code").permitAll()
.anyRequest().authenticated();
return http;
}
/**
* 指定某种运行环境下关闭权限校验;为了方便开发,一般我们的dev环境会关闭接口权限校验
* @return
*/
@Override
public CloseAuthorityEvironment customCloseAuthorityEvironment(){
return new CloseAuthorityEvironment(currentRunEnvironment,"dev");
}
}
/**
* 在资源服务器中配置第三方应用访问权限,第三放client拥有scopes权限则可以访问
* 用户自定义配置,子类可覆盖自定义实现
* @param http
* @throws Exception
*/
@Override
protected HttpSecurity customConfigure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/code").permitAll()
.antMatchers( "/read").access("#oauth2.hasScope('read')")
.antMatchers( "/write").access("#oauth2.hasScope('write')")
.anyRequest().authenticated();
return http;
}
- 1、继承
SecurityProperties
@Component
public class FrameworkProperties extends SecurityProperties {
}
- 2、继承
TokenStoreConfig
使 token 配置生效,token 存储默认使用 jwt,通过配置可修改为 redis 存储
@Component
public class TokenConfig extends TokenStoreConfig {
}
- 配置 client 参数
#spring security oauht2 参数说明见2.5.10
framework:
security:
oauth2:
clients[0]:
clientId: client
clientSecret: clientSecret
accessTokenValiditySeconds: 604800
refreshTokenValiditySeconds: 2592000
authorizedGrantTypes: ["refresh_token", "password"]
redirectUris: "http://example.com"
scopes: ["all", "read", "write"]
tokenStore: jwt
jwtSigningKey: jwtSecret
登录接口及刷新 token 接口都需要设置 Authorization 为 Basic Auth,其余接口设置请求头 Authorization 参数为 token 进行访问,密码登录及验证码登录只接受 Json 格式登录,设置请求头Content-Type
为application/json
-
1、密码登录 , path: " http://localhost:8080/login "
-
设置 Authorization
-
设置请求头
Content-Type
为application/json
-
参数
{ "username": "username", "password": "password" }
-
-
2、短信验证码登录,path: " http://localhost:8080/login/mobile "
-
设置 Authorization
-
设置请求头
Content-Type
为application/json
-
参数
{ "smsCode": "code", "mobile": "mobile" }
-
-
刷新 token,path:" http://localhost:8080/oauth/token "
-
设置设置 Authorization
-
设置请求头
Content-Type
为application/json
-
参数
-
设置 from-data
grant_type = refresh_token refresh_token = refresh_token
-
1、定义授权页面视图
@Controller
@SessionAttributes({ "authorizationRequest" })
public class OAuthController {
@RequestMapping("/custom/confirm_access")
public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
ModelAndView view = new ModelAndView();
view.setViewName("base-grant");
view.addObject("clientId", authorizationRequest.getClientId());
return view;
}
}
// http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.example.com&scope=all
2、在配置文件中配置授权页面路径
framework:
security:
oauth2:
confirm_url: "/custom/confirm_access"
framework:
security:
oauth2:
# 配置认证服务器,可以配置多个clients
clients:
- clientId: client #客户端id
clientSecret: 123qwe #客户端密码
accessTokenValiditySeconds: 604800 #token过期时间
# 客户端授权模式
authorizedGrantTypes:
["refresh_token", "password", "authorization_code"]
# 回调地址
redirectUris: "http://example.com"
# 客户端权限
scopes: ["all", "read", "write"]
- clientId: client2
clientSecret: 123qwe
accessTokenValiditySeconds: 604800
authorizedGrantTypes:
["refresh_token", "password", "authorization_code"]
redirectUris: "http://example.com"
scopes: ["all", "read", "write"]
# token存储类型 默认是jwt 也可以配置为redis
tokenStore: jwt
# token秘钥
jwtSigningKey: secret
# 授权页面路径 不设置默认oauth2授权页面
confirm_url: "/custom/confirm_access"
# token增强信息 可配置多个
tokenInfo:
author: "xxx"
project: "xxxx"
public class ResponseResult {
private int code;
private String msg;
private Object data;
}
参见 2.2
继承GlobalReturnConfig
@EnableWebMvc
@Configuration
@RestControllerAdvice({"com.netx.web.controller"})
public class ControllerReturnConfig extends GlobalReturnConfig {
}
注意:@RestControllerAdvice 要设置扫描拦截包名,如:com.netx.web.controller
,这样就只拦截 controller 包下的类。否则 swagger 也会拦截影响 swagger 正常使用
全局拦截后 Controller 层 API 不需要显示地返回 ResponseResult,因为会全局拦截处理并返回 ResponseResult 格式。
@ApiOperation("新增用户")
@PostMapping
public ResponseResult create(@RequestBody @Valid AddUsertVO addUserVO){
userServiceImpl.saveUser(addUserVO);
return ResponseResult.success();
}
可以改成
@ApiOperation("新增用户")
@PostMapping
public void createAgents(@RequestBody @Valid AddUsertVO addUserVO){
userServiceImpl.saveUser(addUserVO);
}
代码
@ApiOperation("获取用户列表")
@GetMapping("/list")
public ResponseResult pageUsers(UserSearch search) {
IPage<UserVO> page = userServiceImpl.pageUsers(search);
return ResponseResult.success(page);
}
可以改成
@ApiOperation("获取用户列表")
@GetMapping("/list")
public IPage<UserVO> pageUsers(UserSearch search) {
return userServiceImpl.pageUsers(search);
}