java作為後端:vue結合datatables後端分頁
阿新 • • 發佈:2018-12-20
官網:
英文:https://datatables.net/
中文:http://www.datatables.club/
引入datatables的CSS和JS檔案
css: <link href="statics/css/dataTable/dataTables.bootstrap.min.css" type="text/css" rel="stylesheet"> js: <script src="statics/js/dataTable/jquery.dataTables.min.js" type="text/javascript"></script> <script src="statics/js/dataTable/dataTables.bootstrap.min.js" type="text/javascript"></script>
其他的相關引入自己定義樣式。
結合個人專案例項HTML部分
<section class="content" id="app"> <div class="row"> <div class="col-lg-12"> <div class="callout callout-info"> <h4>使用者管理</h4> <p>系統使用者進行管理,包括使用者的新增、修改、刪除等操作。</p> </div> </div> </div> <div class="box box-primary collapsed-box"> <div class="box-header with-border"> <button type="button" class="btn btn-info" data-widget="collapse">過濾</button> <button type="button" class="btn btn-info" @click="refresh">重新整理</button> <div class="box-tools pull-right"> <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button> </div><!-- /.box-tools --> </div><!-- /.box-header --> <div class="box-body form-horizontal"> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">使用者名稱</label> <div class="col-sm-8"> <input type="input" class="form-control" v-model="queryParams.username"> </div> </div> </div> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">真實姓名</label> <div class="col-sm-8"> <input type="input" class="form-control" v-model="queryParams.truename"> </div> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">性別</label> <div class="col-sm-8"> <select name="gender" class="code form-control" tag="code_xb" data-live-search="true" v-model="queryParams.gender"> </select> </div> </div> </div> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">啟用</label> <div class="col-sm-8"> <select name="enabled" class="code form-control" tag="code_if" data-live-search="true" v-model="queryParams.enabled"> </select> </div> </div> </div> </div> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">聯絡電話</label> <div class="col-sm-8"> <input type="input" class="form-control" v-model="queryParams.phone"> </div> </div> </div> <div class="col-md-6"> <div class="form-group"> <label class="col-sm-4 control-label">建立時間</label> <div class="col-sm-8"> <input name="createtime" type="input" class="form-control daterange" v-model="queryParams.createtime"> </div> </div> </div> </div> </div><!-- /.box-body --> <div class="box-footer text-center"> <button type="submit" class="btn btn-info" @click="query">查詢</button> <button type="submit" class="btn btn-default" @click="reset">重置</button> </div> </div> <div class="box"> <div class="box-header"> <div class="btn-group"> <button type="button" class="btn btn-info" @click="add">新增</button> </div> </div><!-- /.box-header --> <div class="box-body"> <table id="list" class="table table-hover table-striped"> </table> </div><!-- /.box-body --> </div> </section>
JS部分
<script type="text/javascript"> var vm; vm = new Vue({ el: '#app', data: { queryParams: {}, table: {} }, created: function () { }, mounted: function () { this.bind(); }, methods: { add: function () { layer.open({ type: 2, title: '新增使用者', shadeClose: true, shade: 0.8, area: ['80%', '90%'], content: baseUrl + '/sys/user/edit.html', btn:['確定','取消'], yes: function(index, layero){ var body = layer.getChildFrame('body', index); var iframeWin = window[layero.find('iframe')[0]['name']]; if(iframeWin.submit()){ layer.close(index); } } }); }, prem:function(data){ layer.open({ type: 2, title: '使用者授權', shadeClose: true, shade: 0.8, area: ['400px', '300px'], content: baseUrl +'/user/prem?userid=' + data.uid }); }, eidt:function(data){ layer.open({ type: 2, title: '修改使用者', shadeClose: true, shade: 0.8, area: ['80%', '90%'], content: baseUrl +'/sys/user/edit.html?id=' + data.phone, btn:['確定','取消'], yes: function(index, layero){ var body = layer.getChildFrame('body', index); var iframeWin = window[layero.find('iframe')[0]['name']]; if(iframeWin.submit()){ layer.close(index); } } }); }, del:function(data){ layer.confirm("確認刪除所選使用者嗎?", {btn: ['確定', '取消'], title: "提示"}, function (index) { $.ajax({ type : "GET", url : baseUrl + "/user/del/"+data.phone, async : false, success : function(r){ parent.layer.msg("使用者刪除成功!", { time: 10000 }); vm.refresh(); layer.close(index); } }); }); }, refresh: function () { this.reset(); this.table.ajax.reload(); }, query: function () { this.table.draw(); }, reset: function () { this.queryParams = {}; $(".code").each(function (index) { $(this).val(null).trigger("change"); }); }, bind: function () { var _self = this; this.table = $('#list').DataTable({ ordering: false, processing: true, serverSide: true, autoWidth: true, searching: false, ajax: { type: "GET", url: baseUrl + "/user/list", data: function (d) { d.params = JSON.stringify(_self.queryParams); }, dataSrc: "data", dataType: "json" }, language: { processing: "載入中...", zeroRecords:"沒有匹配結果", emptyTable:"沒有匹配結果", infoEmpty:"無符合條件記錄", info: "顯示第 _START_ 至 _END_ 條結果,共_TOTAL_條記錄", lengthMenu: "每頁顯示 _MENU_ 條記錄", paginate: {"first": "首頁 ", "last": "末頁", "next": "下一頁", "previous": "上一頁"} }, drawCallback:function (settings, data){ $(".edit").bind("click",function () { var data = vm.table.row($(this).parents('tr')).data(); vm.eidt(data); }) $(".del").bind("click",function () { var data = vm.table.row($(this).parents('tr')).data(); vm.del(data); }) $(".prem").bind("click",function () { var data = vm.table.row($(this).parents('tr')).data(); vm.prem(data); }) }, columns: [ {data: "username", title: "使用者名稱"}, {data: "truename", title: "真實姓名"}, {data: "phone", title: "聯絡電話"}, {data: "gender", title: "性別"}, {data: "enabled", title: "啟用"}, { data: "createtime", title: "建立時間", render : function(data, type, full, meta) { //時間格式化 return moment(data).format("YYYY-MM-DD HH:mm:ss"); } }, { title: "操作", render : function(data, type, full, meta) { return "<div class=\"btn-group\"><button type=\"button\" class=\"btn btn-danger edit\">修改</button><button type=\"button\" class=\"btn btn-danger del\">刪除</button><button type=\"button\" class=\"btn btn-danger prem\">授權</button></div>"; } } ] }); if ($(".code").length > 0) { $(".code").each(function (index) { $(this).select2({ placeholder: "請選擇", ajax: { url: baseUrl + '/dict/info/' +$(this).attr("tag"), processResults: function (r) { var displayArray = new Array(); r.data.forEach(function (val) { displayArray.push({id:val.code, text:val.name}); }); return { results: displayArray }; } } }); $(this).on("change", function (e) { vm.queryParams[$(this).attr("name")] = $(this).val(); }); }); } if ($(".daterange").length > 0) { lay(".daterange").each(function (index, el) { laydate.render({ elem: this, range: true, done: function (value, date) { vm.queryParams[$(el).attr("name")] = value; $("input[name='" + $(el).attr("name") + "']").val(value).change(); } }); }); } } } }); </script>
彈窗部分HTML
<section class="content" id="app">
<div class="box box-primary">
<div class="box-body">
<form id="entityForm">
<div class="form-group">
<label class="control-label">使用者名稱</label>
<input type="text" class="form-control" v-model="entity.username" name="username">
</div>
<div class="form-group">
<label class="control-label">真實姓名</label>
<input type="text" class="form-control" v-model="entity.truename" name="truename">
</div>
<div class="form-group">
<label class="control-label">手機號碼</label>
<input type="text" class="form-control" v-model="entity.phone" name="phone">
</div>
<div class="form-group">
<label class="control-label">性別</label>
<select name="gender" class="code form-control" tag="code_xb" data-live-search="true" v-model="entity.gender" name="gender">
</select>
</div>
<div class="form-group">
<label class="control-label">是否啟用</label>
<select name="enabled" class="code form-control" tag="code_if" data-live-search="true" v-model="entity.enabled" name="enabled">
</select>
</div>
<div class="form-group">
<label class="control-label">密碼</label>
<input type="password" class="form-control" v-model="entity.password" name="password">
</div>
<div class="form-group">
<label class="control-label">重複密碼</label>
<input type="password" class="form-control" v-model="entity.repassword" name="repassword">
</div>
</form>
</div><!-- /.box-body -->
</div>
</section>
彈窗部分的JS
<script type="text/javascript">
var form;
var dicts;
var vm;
$(document).ready(function () {
form = $("#entityForm");
var config = {
feedbackIcons: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
fields: {
username: {
message: '請輸入使用者名稱',
validators: {
notEmpty: {
message: '請輸入使用者名稱'
}
}
},
truename: {
message: '請輸入真實姓名',
validators: {
notEmpty: {
message: '請輸入真實姓名'
}
}
},
phone: {
message: '請輸入手機號碼',
validators: {
notEmpty: {
message: '請輸入手機號碼'
}
}
},
password: {
message: '請輸入密碼'
},
repassword: {
message: '請重複輸入密碼',
validators: {
identical: {
field: 'password',
message: '兩次輸入的密碼不相符'
}
}
},
gender: {
message: '請選擇性別',
validators: {
notEmpty: {
message: '請選擇性別'
}
}
},
enabled: {
message: '請選擇是否啟用',
validators: {
notEmpty: {
message: '請選擇是否啟用'
}
}
}
}
};
if(getRequestParam("id") == null){
config.fields.phone.validators.remote = {
url: baseUrl + '/check',
message: '使用者已存在',
delay : 2000,
type: 'GET'
};
config.fields.password.validators ={
notEmpty: {
message: '請輸入密碼'
}
};
config.fields.repassword.validators.notEmpty = {
message: '請重複輸入密碼'
};
}else{
$("input[name=phone]").attr("disabled",true);
}
form.bootstrapValidator(config);
});
loadDic("code_xb,code_if",function (r) {
dicts = r.data;
vm = new Vue({
el: '#app',
data: {
queryParams:{},
table:{},
primaryKey:getRequestParam("id"),
entity:{},
},
created:function(){
if(this.primaryKey != null){
this.info();
}
},
mounted:function(){
this.bind();
},
methods:{
bind:function () {
var _self = this;
$(".code").each(function (index) {
var tag = $(this).attr("tag");
var name = $(this).attr("name");
var displaydata = new Array();
dicts[tag].forEach(function (item) {
displaydata.push({id:item.code, text:item.name});
});
var select = $(this).select2({
data: displaydata,
placeholder:'請選擇'
});
select.val(_self.entity[name]).trigger('change');
select.on("change",function (e) {
_self.entity[name] = $(this).val();
});
});
},
save:function(callback){
var bv = form.data('bootstrapValidator');
bv.validate();
if(bv.isValid()) {
var url = baseUrl + (this.primaryKey == null ? "/user/add" : "/user/update");
var title = (this.primaryKey == null ? "新增使用者成功" : "修改使用者成功");
$.ajax({
type: 'POST',
url: url,
contentType: 'application/json;charset=utf-8',
dataType : 'json',
data: JSON.stringify(this.entity),
success: function (r) {
if(r.success){
parent.layer.msg(title, {
time: 10000
});
parent.vm.refresh();
var index = parent.layer.getFrameIndex(window.name); //獲取視窗索引
parent.layer.close(index); // 關閉layer
}
}
});
}
return false;
},
info:function(){
var _self = this;
$.ajax({
type : "GET",
url : baseUrl + "/user/info/"+ this.primaryKey,
async : false,
success : function(r){
_self.entity = r.data;
}
});
}
}
});
})
function submit() {
return vm.save();
}
</script>
後端分頁controller部分
@RequestMapping("/user")
@RestController
public class UserController extends BaseController{
@Autowired
private SysUserService sysUserService;
/**
*使用者列表
* @param page
* @return
*/
@RequestMapping("list")
@ResponseBody
@RequiresPermissions("sys:user:list")
public PageData list(Page page) {
return sysUserService.getUserList(page);
}
/**
*使用者詳情
* @param phone
* @return
*/
@RequestMapping("info/{phone}")
@ResponseBody
@RequiresPermissions("sys:user:info")
public Message info(@PathVariable String phone) {
SysUser sysUser = sysUserService.getUserByPhone(phone);
sysUser.setPassword("");
return Message.success(sysUser);
}
/**
*新增使用者
* @param sysUser
* @return
*/
@RequestMapping("add")
@ResponseBody
@RequiresPermissions("sys:user:add")
public Message Message(@RequestBody SysUser sysUser)
{
sysUserService.addUser(sysUser);
return Message.success();
}
/**
*修改使用者
* @param sysUser
* @return
*/
@RequestMapping("update")
@ResponseBody
@RequiresPermissions("sys:user:update")
public Message update(@RequestBody SysUser sysUser)
{
sysUserService.updateUser(sysUser);
return Message.success();
}
/**
* 刪除使用者
* @param phone
* @return
*/
@RequestMapping("del/{phone}")
@ResponseBody
@RequiresPermissions("sys:user:del")
public Message del(@PathVariable String phone)
{
sysUserService.delUser(phone);
return Message.success();
}
}
service部分
@Service
public class SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
/**
* 登入
* @param sysUser
* @return
*/
public Message login(SysUser sysUser){
Message message = Message.N();
UsernamePasswordToken token=new UsernamePasswordToken(sysUser.getPhone(), sysUser.getPassword());
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token); //完成登入
message.setSuccess(true);
message.setMessage("登入成功!");
} catch(Exception e) {
message.setSuccess(false);
message.setMessage("使用者名稱或密碼錯誤!");
}
return message;
}
/**
* 新增使用者
* @param sysUser
*/
@CacheEvict(value = "sysusers", key="#sysUser.phone")
public void addUser(SysUser sysUser){
ByteSource credentialsSalt = ByteSource.Util.bytes(sysUser.getPhone());
Object obj = new SimpleHash("MD5", sysUser.getPassword(), credentialsSalt, 1024);
sysUser.setPassword(((SimpleHash) obj).toHex());
sysUser.setId(UUID.randomUUID().toString().replace("-",""));
sysUserMapper.insert(sysUser);
}
/**
* 刪除使用者
* @param phone
*/
@CacheEvict(value = "sysusers", key="#phone")
public void delUser(String phone){
Wrapper<SysUser> sysUserWrapper = new EntityWrapper<>();
sysUserWrapper.eq("phone",phone);
SysUser sysUser = new SysUser();
sysUser.setDeltag("1");
sysUserMapper.update(sysUser, sysUserWrapper);
}
/**
*修改使用者
* @param sysUser
*/
@CacheEvict(value = "sysusers", key="#sysUser.phone")
public void updateUser(SysUser sysUser){
if(sysUser.getPassword() != null && sysUser.getPassword().length() > 0){
ByteSource credentialsSalt = ByteSource.Util.bytes(sysUser.getPhone());
Object obj = new SimpleHash("MD5", sysUser.getPassword(), credentialsSalt, 1024);
sysUser.setPassword(((SimpleHash) obj).toHex());
}else{
sysUser.setPassword(null);
}
sysUserMapper.updateById(sysUser);
}
/**
* 根據手機號碼獲取使用者
* @param phone
* @return
*/
@Cacheable(value = "sysusers",key = "#phone")
public SysUser getUserByPhone(String phone){
Wrapper<SysUser> sysUserWrapper = new EntityWrapper<SysUser>();
sysUserWrapper.eq("phone", phone);
List<SysUser> list = sysUserMapper.selectList(sysUserWrapper);
SysUser sysUser = null;
if(list != null && list.size() > 0){
sysUser = list.get(0);
sysUser.setMenus(getUserMenus(sysUser.getId()));
}
return sysUser;
}
/**
* 獲取使用者選單列表
* @param uid
* @return
*/
private List<SysMenu> getUserMenus(String uid){
List<SysMenu> menus = sysUserMapper.getUserPermissionById(uid);
List<SysMenu> tree = new ArrayList<SysMenu>();
if(menus != null && menus.size() > 0){
for (SysMenu menu:
menus) {
if(menu.getIsfunc() == null){
if(menu.getPid() == null){
getSubMenu(menu, menus);
tree.add(menu);
}
}
}
}
return tree;
}
/**
* 遍歷子選單
* @param menu
* @param menus
*/
private void getSubMenu(SysMenu menu, List<SysMenu> menus){
for (SysMenu sub:
menus) {
if (sub.getPid() == menu.getId()) {
if (menu.getSubs() == null) {
List<SysMenu> subitems = new ArrayList<SysMenu>();
subitems.add(sub);
menu.setSubs(subitems);
} else {
menu.getSubs().add(sub);
}
getSubMenu(sub, menus);
}
}
}
/**
* 獲取使用者列表
* @param page
* @return
*/
public PageData getUserList(Page page){
List<Filed> queryFileds = new ArrayList<Filed>();
Map<String,Object> params = page.getParams();
if(params != null && params.size() > 0){
queryFileds.add(new TextFiled("username",params));
queryFileds.add(new TextFiled("truename",params));
queryFileds.add(new TextFiled("phone",params));
queryFileds.add(new NumberFiled("gender",params));
queryFileds.add(new NumberFiled("enabled",params));
queryFileds.add(new DateRangeFiled("createtime",params));
}
PageData pageData = new PageData();
String[] cmdSqlArray = QueryUntil.getQuerySql("*", queryFileds," and deltag='0' ","sys_user");
page.setExec_sql(cmdSqlArray[0]);
pageData.setData(sysUserMapper.execSQL(page));
page.setExec_sql(cmdSqlArray[1]);
pageData.setRecordsTotal(Integer.parseInt(sysUserMapper.execSQL(page).get(0).get("count").toString()));
pageData.setRecordsFiltered(pageData.getRecordsTotal());
return pageData;
}
}
Mapper部分
@Mapper
@Repository
public interface SysUserMapper extends BaseMapper<SysUser> {
/**
* 獲取使用者許可權
* @param uid
* @return
*/
List<SysMenu> getUserPermissionById(String uid);
List<Map> execSQL(Page page);
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace必須指向Dao介面 -->
<mapper namespace="com.chinahotelhelp.shm.businessmanagement.module.sys.mapper.SysUserMapper">
<select id="getUserPermissionById" parameterType="string" resultType="com.chinahotelhelp.shm.businessmanagement.module.sys.entity.SysMenu">
SELECT
*
FROM
sys_menu m
WHERE
m.id IN (
SELECT
rm.mid
FROM
sys_user_role ur
LEFT JOIN sys_role_menu rm ON ur.rid = rm.rid
WHERE
ur.uid = #{uid}
)
</select>
<select id="execSQL" parameterType="com.chinahotelhelp.shm.businessmanagement.module.sys.entity.Page" resultType="map">
${exec_sql}
</select>
</mapper>
QueryUntil SQL拼接類
public class QueryUntil {
/**
* 獲取查詢語句
* @param select
* @param fileds
* @param cond
* @param tablename
* @return
*/
public static String[] getQuerySql(String select, List<Filed> fileds, String cond, String tablename){
StringBuffer sb = new StringBuffer();
StringBuffer querySb = new StringBuffer();
StringBuffer queryCountSb = new StringBuffer();
querySb.append("select * from (select " + select + " from " + tablename + " where 1= 1");
if(fileds != null && fileds.size() > 0){
for (Filed filed:
fileds) {
sb.append(filed.getQuery());
}
}
sb.append(cond);
queryCountSb.append("select count(1) as count from " + tablename);
queryCountSb.append(" where 1=1 ");
querySb.append(sb.toString());
queryCountSb.append(sb.toString());
querySb.append(" ) t limit #{start},#{length}");
return new String[]{querySb.toString(), queryCountSb.toString()};
}
/**
* 獲取查詢統計語句
* @param select
* @param fileds
* @param tablename
* @return
*/
public static String getCountQuerySql(String select, List<Filed> fileds, String tablename){
StringBuffer sb = new StringBuffer();
StringBuffer querySb = new StringBuffer();
querySb.append("select " + select + " from " + tablename + " where 1= 1");
if(fileds != null && fileds.size() > 0){
for (Filed filed:
fileds) {
sb.append(filed.getQuery());
}
}
querySb.append(sb.toString());
return querySb.toString();
}
}
JSONUtil json轉換類
public class JSONUtil {
/**
* 字串轉Map
* @param jsonstr
* @return
*/
public static Map<String,Object> parseMap(String jsonstr){
Map<String,Object> result = new HashMap<String,Object>();
JSONObject obj = JSONObject.parseObject(jsonstr);
for (String key:
obj.keySet()) {
result.put(key,obj.get(key));
}
return result;
}
}
實體類
@TableName("sys_user")
public class SysUser implements Serializable {
private static final long serialVersionUID = 8841433872811285796L;
@Id
private String id;
private String username;
private String password;
private String truename;
private String phone;
private String gender;
private String enabled;
private Date createtime;
private String createuser;
private Date modifytime;
private String modifyuser;
private String deltag;
@TableField(exist = false)
private List<SysMenu> menus;
省略get set
ShiroRealm
public class ShiroRealm extends AuthorizingRealm {
@Autowired
@Lazy
private SysUserService sysUserService;
private AuthenticationInfo authenticationInfo;
/**
* 許可權
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
for (SysMenu sysMenu:
sysUser.getMenus()) {
if(sysMenu.getPremission() != null && sysMenu.getPremission().length() > 0){
String[] premissionArray = sysMenu.getPremission().split(",");
for (String item:
premissionArray) {
authorizationInfo.addStringPermission(item);
}
}
if(sysMenu.getSubs() != null && sysMenu.getSubs().size() > 0){
whileSubPrem(sysMenu, authorizationInfo);
}
}
return authorizationInfo;
}
protected void whileSubPrem( SysMenu sysMenu, SimpleAuthorizationInfo authorizationInfo){
for (SysMenu menuItem:
sysMenu.getSubs()) {
if(menuItem.getPremission() != null && menuItem.getPremission().length() > 0){
String[] premissionArray = menuItem.getPremission().split(",");
for (String item:
premissionArray) {
authorizationInfo.addStringPermission(item);
}
}
if(menuItem.getSubs() != null && menuItem.getSubs().size() > 0){
whileSubPrem(menuItem, authorizationInfo);
}
}
}
/**
* 授權
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String phone = usernamePasswordToken.getUsername();
SysUser sysUser = sysUserService.getUserByPhone(phone);
if(sysUser != null){
authenticationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(),
ByteSource.Util.bytes(sysUser.getPhone()), getName());
}else{
throw new AuthenticationException();
}
return authenticationInfo;
}
}
shiro配置
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設定 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 登入成功後要跳轉的連結
shiroFilterFactoryBean.setSuccessUrl("/main.html");
// 未授權介面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
// 攔截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不會被攔截的連結 順序判斷
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
// 配置退出過濾器,其中的具體的退出程式碼Shiro已經替我們實現了
filterChainDefinitionMap.put("/logout", "logout");
// <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;
// <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 密碼校驗規則HashedCredentialsMatcher
* 這個類是為了對密碼進行編碼的 ,
* 防止密碼在資料庫裡明碼儲存 , 當然在登陸認證的時候 ,
* 這個類也負責對form裡輸入的密碼進行編碼
* 處理認證匹配處理器:如果自定義需要實現繼承HashedCredentialsMatcher
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式為MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次數
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean("shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")//可選
public ShiroRealm shiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setAuthorizationCachingEnabled(false);
shiroRealm.setCredentialsMatcher(matcher);
return shiroRealm;
}
/**
* 定義安全管理器securityManager,注入自定義的realm
* @param shiroRealm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(shiroRealm);
return manager;
}
/**
* Spring的一個bean , 由Advisor決定對哪些類的方法進行AOP代理 .
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
/**
* 配置shiro跟spring的關聯
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* lifecycleBeanPostProcessor是負責生命週期的 , 初始化和銷燬的類
* (可選)
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
redis配置
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* Logger
*/
private static final Logger lg = LoggerFactory.getLogger(RedisConfig.class);
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
@Bean
@Override
public KeyGenerator keyGenerator() {
// 設定自動key的生成規則,配置spring boot的註解,進行方法級別的快取
// 使用:進行分割,可以很多顯示出層級關係
// 這裡其實就是new了一個KeyGenerator物件,只是這是lambda表示式的寫法,我感覺很好用,大家感興趣可以去了解下
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(":");
sb.append(method.getName());
for (Object obj : params) {
sb.append(":" + String.valueOf(obj));
}
String rsToUse = String.valueOf(sb);
lg.info("自動生成Redis Key -> [{}]", rsToUse);
return rsToUse;
};
}
@Bean
@Override
public CacheManager cacheManager() {
// 初始化快取管理器,在這裡我們可以快取的整體過期時間什麼的,我這裡預設沒有配置
lg.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(jedisConnectionFactory);
return builder.build();
}
@Bean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory ) {
//設定序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer); // key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value序列化
redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Override
@Bean
public CacheErrorHandler errorHandler() {
// 異常處理,當Redis發生異常時,列印日誌,但是程式正常走
lg.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
lg.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
lg.error("Redis occur handleCachePutError:key -> [{}] value -> [{}]", key, value);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
lg.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
lg.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
/**
* 此內部類就是把yml的配置資料,進行讀取,建立JedisConnectionFactory和JedisPool,以供外部類初始化快取管理器使用
* 不瞭解的同學可以去看@ConfigurationProperties和@Value的作用
*
*/
@ConfigurationProperties
class DataJedisProperties{
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Bean
JedisConnectionFactory jedisConnectionFactory() {
lg.info("Create JedisConnectionFactory successful");
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(host);
factory.setPort(port);
factory.setTimeout(timeout);
factory.setPassword(password);
return factory;
}
@Bean
public JedisPool redisPoolFactory() {
lg.info("JedisPool init successful,host -> [{}];port -> [{}]", host, port);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
return jedisPool;
}
}
}
RabbitConfig 配置
@Configuration
public class RabbitConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private int port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
public final static String QUEUE_NAME = "spring-boot-queue";
public final static String EXCHANGE_NAME = "spring-boot-exchange";
public final static String ROUTING_KEY = "spring-boot-key";
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
// 建立佇列
@Bean
public Queue queue() {
return new Queue(QUEUE_NAME);
}
// 建立一個 topic 型別的交換器
@Bean
public DirectExchange exchange() {
return new DirectExchange(EXCHANGE_NAME);
}
// 使用路由鍵(routingKey)把佇列(Queue)繫結到交換器(Exchange)
@Bean
public Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
}
效果圖如下