官网:
英文: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);
}
}
效果图如下