SpringSecurity,管认证与授权
SpringSession+Redis 分布式session共享
Mybatis-Plus 负责Repository
Swwager 负责前后端分离RESTful文档
POM
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
com.alibaba
fastjson
1.2.45
mysql
mysql-connector-java
runtime
com.baomidou
mybatis-plus-boot-starter
3.3.2
io.springfox
springfox-swagger2
2.8.0
io.springfox
springfox-swagger-ui
2.8.0
com.alibaba
druid
1.1.9
org.slf4j
log4j-over-slf4j
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.0.4:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.jackson.time-zone=GMT+8
spring.redis.password=123456
spring.redis.port=6379
spring.redis.host=192.168.0.189
#druid 下面为连接池的补充设置,应用到上面所有数据源中
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.maxOpenPreparedStatements=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
Swagger
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringBoot API Doc")
.description("This is a restful api document of Spring Boot.")
.version("1.0")
.build();
}
}
Security
/**
* @author cmy
* @date 2020/10/14 16:13
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
UserService userService;
@Autowired
CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;
@Autowired
CustomUrlDecisionManager customUrlDecisionManager;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers( "/swagger**/**", "/webjars/**", "/v2/**","/druid/**" ,"/favicon.ico" );
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor() {
@Override
public O postProcess(O object) {
object.setAccessDecisionManager(customUrlDecisionManager);
object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
return object;
}
})
.and()
.formLogin()
.successHandler((req,resp,authentication) -> {
Map map = new HashMap<>();
map.put("user",authentication.getPrincipal());
ResultUtil.responseJson(resp,ResultUtil.resultSuccess(map));
})
.failureHandler((req,resp,failed) -> {
String msg;
if(failed instanceof LockedException){
msg = "账户被锁定,请联系管理员!";
}else if (failed instanceof BadCredentialsException){
msg = "用户名或者密码输入错误,请重新输入!";
}else if(failed instanceof DisabledException){
msg = "账户被禁用,请联系管理员!";
}else if (failed instanceof AccountExpiredException){
msg = "账户过期,请联系管理员!";
}else if (failed instanceof CredentialsExpiredException){
msg = "密码过期,请联系管理员!";
}else{
msg = "other";
}
resp.setStatus(400);
ResultUtil.responseJson(resp,ResultUtil.resultCode(400,msg));
})
.permitAll()
.and()
.logout()
.logoutSuccessHandler((req,resp,authentication) -> {
ResultUtil.responseJson(resp,ResultUtil.resultCode(200,"注销登录成功"));
})
.permitAll()
.and().csrf().disable()
.exceptionHandling().authenticationEntryPoint((req,resp,exception) -> {
resp.setStatus(401);
if (exception instanceof InsufficientAuthenticationException) {
ResultUtil.responseJson(resp,ResultUtil.resultCode(401,"尚未登录,请登录"));
}else{
ResultUtil.responseJson(resp,ResultUtil.resultCode(401,"请求失败,请联系管理员!"));
}
})
.accessDeniedHandler((req,resp,exception) -> {
resp.setStatus(401);
ResultUtil.responseJson(resp,ResultUtil.resultCode(401,exception.getMessage()));
});
}
}
如果需要限制单点登录的,可以加上
@Autowired
FindByIndexNameSessionRepository sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest()
...
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry());
}
@Bean
SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(sessionRepository);
}
Mybatis-Plus分页
@EnableTransactionManagement
@Configuration
@MapperScan("com.anruisi.securitydemo.mapper")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
配置连接池
@Configuration
public class DruidConfig {
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.filters}")
private String filters;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Bean
@Primary
public DataSource dataSource(){
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
new RuntimeException("druid阿里数据库连接池 初始化失败");
}
datasource.setConnectionProperties(connectionProperties);
return datasource;
}
/**
* 配置监控服务器
*
* @return 返回监控注册的servlet对象
*/
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 添加IP白名单
servletRegistrationBean.addInitParameter("allow", "192.168.0.136,127.0.0.1");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
servletRegistrationBean.addInitParameter("deny", "192.168.0.4");
// 添加控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "admin123");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 配置服务过滤器
*
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
return filterRegistrationBean;
}
}
DEMO下载