從零開始搭建博客01----框架搭建,權(quán)限控制
摘要
本期主要完成了集成mybatis plus、lombok,Redis,做好全局異常處理,并且把layui社區(qū)的頁面集成到項目中,然后就是完成首頁的渲染。
環(huán)境
項目結(jié)構(gòu)
集成MyBatis plus
說明
MyBatis Plus 是一個持久層框架,是在MyBatis 之上做的一層封裝,通過對Dao層,Servcie層通用代碼的封裝,極大的簡化了開發(fā)。
詳情參考:https://github.com/baomidou/mybatis-plus
步驟一 添加依賴
步驟一:在pom文件中添加 MyBatis Plus 的依賴,使用最新版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
步驟二. 數(shù)據(jù)庫連接配置
步驟二 新建數(shù)據(jù)fly_blog,并在application.yml 文件中添加數(shù)據(jù)庫連接配置。
DataSource Config
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/fly_blog
username: root
password: admin
步驟三. 配置Mapper的掃描路徑
在程序的入口類FlyBlogApplication 添加@MapperScan(value = “com.fly.dao”) 注解
@SpringBootApplication
@MapperScan(value = “com.fly.dao”)
public class FlyBlogApplication {
public static void main(String[] args) {
SpringApplication.run(FlyBlogApplication.class, args);
log.info("系統(tǒng)啟動成功");
}
}
至此,MyBatis Plus 就集成完畢,在3.0.1 版本的MyBatis Plus中不需要配置xml的位置。
放下以下兩個位置均可
位置一:
位置二
MyBatisPlus 還給我們提供了一個特別實用的功能。自動生成代碼,它可以自動生成dao,model,mapper,service,controller的代碼。
自動生成代碼配置
生成代碼啟動類
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ResourceBundle;
/**
-
Created by Lucare.Feng on 2017/2/23.
*/
public class MyGenerator {/**
-
- MySQL 生成演示
-
*/
public static void main(String[] args) {//用來獲取Mybatis-Plus.properties文件的配置信息 ResourceBundle rb = ResourceBundle.getBundle("Mybatis-Plus"); AutoGenerator mpg = new AutoGenerator(); String systemDir = System.getProperty("user.dir"); // 全局配置 GlobalConfig gc = new GlobalConfig(); gc.setOutputDir(systemDir + rb.getString("OutputDir")); gc.setFileOverride(true); gc.setActiveRecord(true); gc.setEnableCache(false);// XML 二級緩存 gc.setBaseResultMap(true);// XML ResultMap gc.setBaseColumnList(false);// XML columList gc.setAuthor(rb.getString("author")); // 自定義文件命名,注意 %s 會自動填充表實體屬性! String classPrefix = "%s"; gc.setMapperName(classPrefix+"Mapper"); gc.setXmlName(classPrefix+"Mapper"); gc.setServiceName(classPrefix+"Service"); gc.setServiceImplName(classPrefix+"ServiceImpl"); gc.setControllerName(classPrefix+"Controller"); mpg.setGlobalConfig(gc); // 數(shù)據(jù)源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL);
-
// dsc.setTypeConvert(new MySqlTypeConvert());
// dsc.setTypeConvert(new MySqlTypeConvert() {
// // 自定義數(shù)據(jù)庫表字段類型轉(zhuǎn)換【可選】
// @Override
// public DbColumnType processTypeConvert(String fieldType) {
// System.out.println(“轉(zhuǎn)換類型:” + fieldType);
// return super.processTypeConvert(fieldType);
// }
// });
dsc.setDriverName(“com.mysql.jdbc.Driver”);
dsc.setUsername(rb.getString(“userName”));
dsc.setPassword(rb.getString(“passWord”));
dsc.setUrl(rb.getString(“url”));
mpg.setDataSource(dsc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
- 1
- 2
// strategy.setTablePrefix(new String[]{“t_”});// 此處可以修改為您的表前綴
// strategy.setNaming(NamingStrategy.remove_prefix_and_camel);// 表名生成策略
// strategy.setNaming(NamingStrategy.removePrefixAndCamel());// 表名生成策略
// strategy.setInclude(new String[]{“shop_create_order_record”}); // 需要生成的表
strategy.setInclude(rb.getString(“tableName”).split(",")); // 需要生成的表
// String[] strings = {“mto_users”, “shiro_permission”};
// strategy.setInclude(strings); // 需要生成的表
// strategy.setExclude(new String[]{“t_rong_bid”}); // 排除生成的表
// 字段名生成策略
strategy.setNaming(NamingStrategy.underline_to_camel);
// 自定義實體父類
// strategy.setSuperEntityClass(“hello.entity.BaseEntity”);
// 自定義實體,公共字段
// strategy.setSuperEntityColumns(new String[]{“id”});
// 自定義 mapper 父類
// strategy.setSuperMapperClass(“com.fcs.demo.TestMapper”);
// 自定義 service 父類
// strategy.setSuperServiceClass(“com.fcs.demo.TestService”);
// 自定義 service 實現(xiàn)類父類
// strategy.setSuperServiceImplClass(“com.fcs.demo.TestServiceImpl”);
// 自定義 controller 父類
// strategy.setSuperControllerClass(“com.risk.controller.BaseController”);
// 【實體】是否生成字段常量(默認(rèn) false)
// public static final String ID = “test_id”;
// strategy.setEntityColumnConstant(true);
// 【實體】是否為構(gòu)建者模型(默認(rèn) false)
// public User setName(String name) {this.name = name; return this;}
// strategy.setEntityBuliderModel(true);
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent(rb.getString("parent"));
pc.setModuleName("");
pc.setController("controller");// 這里是控制器包名,默認(rèn) web
pc.setEntity("entity");
pc.setMapper("dao");
pc.setXml("mapper");
pc.setService("service");
pc.setServiceImpl("service" + ".impl");
mpg.setPackageInfo(pc);
// 執(zhí)行生成
mpg.execute();
}
}
對應(yīng)的配置文件MyBatis-Plus.properties
#此處為本項目src所在路徑(代碼生成器輸出路徑)
OutputDir=/fly_blog/src/main/java
#數(shù)據(jù)庫表名(此處切不可為空,如果為空,則默認(rèn)讀取數(shù)據(jù)庫的所有表名),多個表用","號分割
tableName=user
#生成代碼類名類名
#className=MtoLog
#設(shè)置作者
author=jay.xiang
#自定義包路徑
parent=com.fly
#正常情況下,下面的代碼無需修改!?。。。。。。。?!
#數(shù)據(jù)庫地址
url=jdbc:mysql://localhost:3306/fly_blog?useUnicode=true&characterEncoding=utf-8&useSSL=false
#數(shù)據(jù)庫用戶名
userName=root
#數(shù)據(jù)庫密碼
passWord=admin
至此,MyBatisPlus的配置就完成了。
集成lombok
添加依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
集成Redis
第一步:導(dǎo)入redis的pom包
`
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
`
第二步:配置redis的連接信息
spring: redis:
sentinel:
master: mymaster
nodes:
第三步:為了讓我們存到redis中的數(shù)據(jù)更容易看懂,我們需要
換一種序列化方式,默認(rèn)的是采用jdk的序列化方式,這里選用Jackson2JsonRedisSerializer,
只需要重寫redisTemplate操作模板的生成方式即可。新建一個config包,放在這個包下。
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(new ObjectMapper());
template.setKeySerializer(jackson2JsonRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
全局異常處理
步驟一. 自定義異常類
public class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
步驟二. 定義全局異常處理,使用@ControllerAdvice表示
定義全局控制器異常處理,使用@ExceptionHandler表示
針對性異常處理,可對每種異常針對性處理
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandle {
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) {
log.error("------------------>捕獲到全局異常", e);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject(“exception”, e);
modelAndView.addObject(“url”, req.getRequestURI());
modelAndView.setViewName(“error”);
return modelAndView;
}
@ExceptionHandler(value = MyException.class)
@ResponseBody
public R jsonErrorHandler(HttpServletRequest req, MyException e) {
return R.failed(e.getMessage());
}
}
數(shù)據(jù)庫設(shè)計
sql語句參見: flyblog.sql
以上就是分支1內(nèi)容[v1-basecode]
----------------------分支二內(nèi)容[v2-shiro-login]----------------------------------
##集成Shiro
步驟一,引入pom文件
<!--集成shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
Realm: 認(rèn)證與授權(quán)
SecurityManager:Shiro架構(gòu)的核心,協(xié)調(diào)內(nèi)部各個安全組件之間的交互。
步驟二:配置Shiro的SecurityManager核心和過濾器
@Slf4j
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public SecurityManager securityManager(OAuth2Realm oAuth2Realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(oAuth2Realm);
log.info("------------->securityManager注入完成");
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
// 配置登錄的url和登錄成功的url
filterFactoryBean.setLoginUrl("/login");
filterFactoryBean.setSuccessUrl("/user/center");
// 配置未授權(quán)跳轉(zhuǎn)頁面
filterFactoryBean.setUnauthorizedUrl("/error/403");
Map<String, String> hashMap = new LinkedHashMap<>();
hashMap.put("/login", "anon");
hashMap.put("/user*", "user");
hashMap.put("/user/**", "user");
hashMap.put("/post/**", "user");
filterFactoryBean.setFilterChainDefinitionMap(hashMap);
return filterFactoryBean;
}
}
在此處我們重寫了認(rèn)證和授權(quán)的Realm,并將Realm配置到SecurityManager中,
然后shiro的過濾器呢,給Shiro配置了登錄的url,登錄成功url和沒有權(quán)限提示的url,
有配置了需要攔截的url。
而Realm需要繼承AuthorizingRealm并授權(quán)和認(rèn)證方法。
@Slf4j
@Component
public class OAuth2Realm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授權(quán)
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 認(rèn)證
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 注意token.getUsername()是指email!!!
AccountProfile profile = userService.login(token.getUsername(), String.valueOf(token.getPassword()));
log.info("-------------------->進入認(rèn)證步驟");
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
return info;
}
}
這時候shiro已經(jīng)集成到了項目中,啟動項目后會打印 "securityManager注入完成"的提示。
然后我們可以使用SecurityUtils.getSubject()去操作用戶的權(quán)限操作了。
登錄注冊
登錄接口:
@PostMapping("/login")
@ResponseBody
public R doLogin(String email, String password, ModelMap model) {
if (StringUtils.isAnyBlank(email, password)) {
return R.failed(“用戶名或密碼不能為空”);
}
UsernamePasswordToken token = new UsernamePasswordToken(email, SecureUtil.md5(password));
try {
//嘗試登陸,將會調(diào)用realm的認(rèn)證方法
SecurityUtils.getSubject().login(token);
} catch (AuthenticationException e) {
if (e instanceof UnknownAccountException) {
return R.failed("用戶不存在");
} else if (e instanceof LockedAccountException) {
return R.failed("用戶被禁用");
} else if (e instanceof IncorrectCredentialsException) {
return R.failed("密碼錯誤");
} else {
return R.failed("用戶認(rèn)證失敗");
}
}
return R.ok("登錄成功");
}
前端調(diào)用:
<script>
layui.use('form',function(){
var form = layui.form;
// 監(jiān)聽提交
form.on('submit(*)',function (data) {
$.post('/login',data.field,function (res) {
if (res.code==0) {
location.href="/user/center";
}else {
layer.msg(res.msg());
}
});
return false;
});
});
</script>
注冊與登錄類似:
博客分類、分頁
- 首頁分類顯示
由于分類變化較少,所以在項目初始化時候放在上下文猴子那個(ServletContext).
但是這樣也有問題,在web端與后臺管理端不是同一個項目,或者做了負(fù)載均衡的項目,
如果改動了分類信息,因為不同項目不同上下文,所以會出現(xiàn)數(shù)據(jù)不一致的可能。
現(xiàn)階段我們先把分類信息存放在上下文中,后期如果涉及到負(fù)載均衡或者分離時候,
我們可以通過MQ消息隊列的方式來改變上下文內(nèi)容。
博客分類信息是項目啟動之后初始化到上下文,這里涉及到ApplicationRunner接口,
ApplicationRunner接口是在容器啟動成功后的最后一步回調(diào)(類似于開機自啟)。
package org.springframework.boot;
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments var1) throws Exception;
}
我們需要重寫ApplicationRunner類的run方法,因為SpringBoot在啟動完成之后
會調(diào)用這個run方法。所以我們只需要把博客分類的邏輯在run方法里面實現(xiàn)就行。
另外,因為需要用到上下文,所以我們也實現(xiàn)ServletContextAware方法,重寫
serServletContext方法,把serveltContext注入
具體代碼如下:
@Slf4j
@Order(100)
@Component
public class ContextStartup implements ApplicationRunner,ServletContextAware {
private ServletContext servletContext;
@Autowired
CategoryService categoryService;
@Override
public void run(ApplicationArguments args) throws Exception {
servletContext.setAttribute("categorys", categoryService.list(null));
log.info("ContextStartup------------>加載categorys");
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥