目录
3.LoginController:登陆成功把用户放入Session作用域
1.角色crud
1.前提:
使用代码生成器,生成Role,Permission并设置多对多的关系
使用代码生成器,生成Role,Permission并设置多对多的关系
//Employee@ManyToMany@JoinTable(name = "employee_role",
joinColumns = @JoinColumn(name="employee_id"),
inverseJoinColumns = @JoinColumn(name="role_id"))
private Set<Role> roles = new HashSet<>();
//Role@ManyToMany@JoinTable(name="role_permission",
joinColumns = @JoinColumn(name="role_id"),
inverseJoinColumns = @JoinColumn(name="permission_id"))
private List<Permission> permissions = new ArrayList<>();
2.角色中添加权限展示
- <th data-options="field:'permissions',width:100,
formatter:formatPerms">权限</th>
- //返回权限的展示方法
//v:当前数据 r:当前行数据 i:行索引
function formatPerms(v,r,i){
var permsName = "";
for(let o of v){
permsName += o.name +" ";
}
return permsName;}
3.页面的完善form与grid
左(当前权限)右(所有权限)都有一个grid
<!-- 一个弹出框,里面要装一个form表单 --><div id="editDlg" class="easyui-dialog" title="功能操作"
data-options="iconCls:'icon-save',closed:true,modal:true"
style="padding:10px;width: 850px;">
<form id="editForm" method="post">
<!-- 修改的话,这个id是必需的 -->
<input id="roleId" name="id" type="hidden"/>
<table cellpadding="5">
<tr>
<td>
名称:<input class="easyui-validatebox" type="" name="name"
data-options="required:true"></input>
编码:<input class="easyui-validatebox" type="text" name="sn"
data-options="required:true"></input>
</td>
</tr>
</table>
<div class="easyui-layout" style="width:100%;height:400px;">
<div data-options="region:'west'" style="width:50%;">
<table id="rolePermissionGrid">
<thead>
<tr>
<th data-options="field:'name',width:100">名称</th>
<th data-options="field:'sn',width:100">编码</th>
<th data-options="field:'url',width:100">资源路径</th>
</tr>
</thead>
</table>
</div>
<div data-options="region:'center'">
<table id="allPermissionGrid">
<thead>
<tr>
<th data-options="field:'name',width:100">名称</th>
<th data-options="field:'sn',width:100">编码</th>
<th data-options="field:'url',width:100">资源路径</th>
</tr>
</thead>
</table>
</div>
</div>
</form>
<div style="text-align:center;padding:5px">
<!-- 如果这里我改成提交按钮,它就会提交整个页面 -->
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" οnclick="$('#editDlg').dialog('close')">关闭</a>
</div>
</div>
4.为角色添加权限/删除权限
...
var rolePermissionGrid = $("#rolePermissionGrid");var allPermissionGrid = $("#allPermissionGrid");
...
itsource = {
add(){
...
//清空grid中的数据
//loadData:加载本地数据,旧的行将被移除
rolePermissionGrid.datagrid("loadData",[]);
...
},
edit(){
...
if(row){
...
//拷备对应的权限数组
var copyPerms = [...row.permissions];
//解决Grid加显问题
rolePermissionGrid.datagrid("loadData",copyPerms);
}else {
...
},
//通过javascript进行保存
save(){
...
editForm.form('submit', {
//form提交的路径
url:url,
//提交之前你要做什么事件
onSubmit: function(param){
//添加一些提交的额外参数
//1.拿到grid中所有的值
var allRows = rolePermissionGrid.datagrid("getRows");
//2.循环把值放进去
for(var i=0;i<allRows.length;i++){
var row = allRows[i];
param[`permissions[${i}].id`] = row.id;
}
return $(this).form('validate');
},
...
});
},
...
//添加一个权限
addPerms(index, row){
//先拿到角色的所有权限
var allRows = rolePermissionGrid.datagrid("getRows");
//遍历进行比较
for(let o of allRows){
//如果两个权限相等,就什么都不做了
if(o.id == row.id){
$.messager.show({
title:'注意事项',
msg:'这个权限已经存在,无需再进行添加!',
timeout:2000,
showType:'slide'
});
return;
}
}
rolePermissionGrid.datagrid("appendRow",row);
},
//删除对应权限
delPerms(index,row){
rolePermissionGrid.datagrid("deleteRow",index);
}
};
//创建当前角色对应的权限(grid控件)
rolePermissionGrid.datagrid({
fit:true,
fitColumns:true,
singleSelect:true,
border:false,
onDblClickRow:itsource.delPerms
})
//创建拿到所有的权限的grid控件
allPermissionGrid.datagrid({
fit:true,
url:'/permission/page',
fitColumns:true,
singleSelect:true,
pagination:true,
border:false,
onDblClickRow:itsource.addPerms
})
5.解决只增不减的问题
修改的时候只能添加不能减少
@ModelAttribute("editRole")public Role beforeEdit(Long id,String cmd){
//修改的时候才查询(只要有id会就进行一次查询,这是不对的)
if(id!=null && "update".equals(cmd)) {
Role role = roleService.findOne(id);
//把要传过来的关联对象都清空,就可以解决n-to-n的问题
role.getPermissions().clear();
return role;
}
return null;
}
2.权限
1.获取所有权限进行判断
//静态资源放行
public class FilterChainDefinitionMapBuilder {
@Autowired
private IPermissionService permissionService;
public Map<String,String> createFilterChainDefinitionMap(){
Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
//注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
filterChainDefinitionMap.put("/login","anon");
filterChainDefinitionMap.put("*.js","anon");
filterChainDefinitionMap.put("*.css","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/easyui/**","anon");
filterChainDefinitionMap.put("/images/**","anon");
filterChainDefinitionMap.put("/media/**","anon");
filterChainDefinitionMap.put("/kaptcha","anon");
filterChainDefinitionMap.put("/wechat/**","anon");
filterChainDefinitionMap.put("/wechat/callback","anon");
filterChainDefinitionMap.put("/logout","logout"); //不登录也可以访问
//1.拿到数据,放入map中
List<Permission> premission =permissionService.findAll();
//2.便利权限,拿到权限和资源
for (Permission permission:premission){
String url = permission.getUrl();//资源
String sn = permission.getSn();//权限
//把路径和资源放到拦截其中
filterChainDefinitionMap.put(url,"yxbPerms["+sn+"]" );
}
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
}
2.完成权限的判断
1.拿到登录用户----->主体
//AuthenticationInfo:认证; 身份验证; 证明
//登录的时候就会调用这个方法来做验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//身份认证(用户名)
// 1.拿到用户名
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
String username = usernamePasswordToken.getUsername();
// 2.根据用户名到数据库拿到用户
Employee loginUser = employeeService.findByUsername(username);
if(loginUser==null){
return null;
//该用户名不存在
}
//从数据库中拿到密码
Object credentials = loginUser.getPassword();
//加盐的数据 主体
ByteSource byteSource = ByteSource.Util.bytes("itsource");
return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
}
修改代码:
<shiro:user>
欢迎[ <shiro:principal property="username" />]登录,
<a href="${pageContext.request.contextPath}/logout">退出</a>
</shiro:user>
2.UserContext拿到当前登录用户
public class UserContext {
private static final String USER_IN_SESSION = "loginUser";
/**
* 把当前登录用户放入Session
* @param loginUser
*/
public static void setUser(Employee loginUser) {
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute(USER_IN_SESSION, loginUser);
}
/**
* 从Session中获取User
*/
public static Employee getUser() {
Subject subject = SecurityUtils.getSubject();
Employee curentUser = (Employee) subject.getSession().getAttribute(USER_IN_SESSION);
return curentUser;
}
}
3.LoginController:登陆成功把用户放入Session作用域
@Controller
public class LoginController {
//用于完成跳转
@RequestMapping(value="/login",method = RequestMethod.GET)
public String index(){
return "login";
}
//用于完成登录
@RequestMapping(value="/login",method = RequestMethod.POST)
@ResponseBody
public JsonResult login(String username, String password, String code, HttpServletRequest req, HttpServletResponse resp){
/*验证码验证*/
/**
* 获取当前的 Subject. 调用 SecurityUtils.getSubject();
* 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
* 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
* 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
*/
String yzm = req.getParameter("code");
// 验证码未输入
if (yzm == null || "".equals(yzm)) {
// 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
// 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码为空
return new JsonResult(false,"请输入验证码");
}
// 获取SESSION中的验证码
// Kaptcha在生成验证码时会将验证码放入SESSION中
// 默认KEY为KAPTCHA_SESSION_KEY, 可以在Web.xml中配置
String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute("KAPTCHA_SESSION_KEY");
// 比较登录输入的验证码和SESSION保存的验证码是否一致
if (!yzm.equals(sessionCaptcha)) {
// 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
// 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码错误
return new JsonResult(false,"验证码错误");
}
//1.拿到访问的主体(当前登录用户)
Subject subject = SecurityUtils.getSubject();
//2.判断这个用户是否已经登录(通过验证)
if(!subject.isAuthenticated()){
//3.如果没有验证,就要完成登录
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
//4.根据toke完成登录功能
subject.login(token);
}catch (UnknownAccountException e){
System.out.println("用户名不存在!!");
e.printStackTrace();
return new JsonResult(false,"账号或者密码出错!");
}catch (IncorrectCredentialsException e){
System.out.println("密码不存在!");
e.printStackTrace();
return new JsonResult(false,"账号或者密码出错!");
}catch (AuthenticationException e){
System.out.println("登录出错!");
e.printStackTrace();
return new JsonResult(false,"程序发生未知错误!");
}
}
//登陆成功后,把当前登陆用户放到session中
//1.拿到当前用户
Employee loginUser = (Employee) subject.getPrincipal();
//2.当前登陆用户放到sessiom中
UserContext.setUser(loginUser);
return new JsonResult();
}
//登出方法
@RequestMapping("/logout")
public String logout(){
//登出
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login";
}
}
4.根据id拿到权限
A. PermissionRepository
//根据当前登录用户拿到对应的权限
@Query("select distinct p.sn from Employee e join e.roles r join r.permissions p where e.id = ?1")
Set<String> findSnByEmp(Long employeeId);
B.IPermissionService
Set<String> findSnByEmp(Long employeeId);
C.PermissionServiceImpl
@Override
public Set<String> findSnByEmp(Long employeeId) {
return permissionRepository.findSnByEmp(employeeId);}
5.JpaRealm:进行权限的判断
public class JpaRealm extends AuthorizingRealm {
@Autowired
private IEmployeeService employeeService;
@Autowired
private IPermissionService permissionService;
//AuthorizationInfo:授权(是否有权限进入操作)
// 我们只需要把相应的权限交给Shiro,它就会自动比对
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//拿到主体信息(指的就是当前登录用户)
Employee loginUser = UserContext.getUser();
//获取权限资源(这里假设已经根据用户名到数据库中获取到了)
Set<String> permissions = permissionService.findSnByEmp(loginUser.getId());
//permissions.add("employee:index");
//permissions.add("role:index");
//permissions.add("employee:*");
//拿到授权对象,并且所有权限交给它
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
//返回授权对象
return simpleAuthorizationInfo;
}
…
}
3.权限判断Ajax请求----->自定义拦截器
注意:请求的分类
- 跳转页面xxx/index
- Ajax请求返回 {“success”:false,”message”:”没有权限”}
重点:怎么判断Ajax请求?
判断请求头里面是否有X-Requested-With
1.自定义权限过滤器
public class ItSourcePermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
Subject subject = this.getSubject(request, response);
if (subject.getPrincipal() == null) {
//没有登录成功后的操作
this.saveRequestAndRedirectToLogin(request, response);
} else {
//登录成功后没有权限的操作
//1.转成http的请求与响应操作
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
//2.根据请求判断是什么请求
String xRequestedWith = httpRequest.getHeader("X-Requested-With");
if (xRequestedWith != null &&"XMLHttpRequest".equals(xRequestedWith)) {
//3.在这里就代表是ajax请求
//表示ajax请求 {"success":false,"message":"没有权限"} --javaweb
httpResponse.setContentType("text/json; charset=UTF-8");
httpResponse.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
}else {
String unauthorizedUrl = this.getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(401);
}
}
}
return false;
}}
2.shiro中引入自定义拦截器
<!-- 5.shiro的真实过滤器(注:这个名称必需和web.xml的代表过滤器【DelegatingFilterProxy】名称一样) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 登录的url,如果没有登录,你访问的路径会跳到这个页面 -->
<property name="loginUrl" value="/login"/>
<!-- 登录成功的url,如果登录成功,会跳转到这个页面 -->
<property name="successUrl" value="/main"/>
<!-- 没有权限时跳转到这个位置 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!--
配置哪些资源被保护,哪些资源需要权限
anon:不需要登录也可以访问相应的权限
authc:需要权限才能访问
/** :所有文件及其子文件
-->
<!-- <property name="filterChainDefinitions">
<value>
/s/login.jsp = anon
/** = authc
</value>
</property>-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
<!-- 引用自定义的权限过滤器 -->
<property name="filters">
<map>
<entry key="yxbPerms" value-ref="itSourcePermissionsFilter"></entry>
</map>
</property>
</bean>
<!-- 配置自定义权限处理的过滤器-->
<bean id="itSourcePermissionsFilter" class="cn.itsource.wy.shiro.realm.ItSourcePermissionsAuthorizationFilter"></bean>
<!-- 这个bean是帮助咱们获取相应的值:它会到一个工厂bean中通过对应的方法拿到相应的值 -->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
factory-method="createFilterChainDefinitionMap"></bean>
<!-- 配置可以创建 -->
<bean id="filterChainDefinitionMapBuilder" class="cn.itsource.wy.shiro.realm.FilterChainDefinitionMapBuilder"></bean>
3.把路径与资源放到拦截器中去
//静态资源放行
public class FilterChainDefinitionMapBuilder {
@Autowired
private IPermissionService permissionService;
public Map<String,String> createFilterChainDefinitionMap(){
Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
//注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
filterChainDefinitionMap.put("/login","anon");
filterChainDefinitionMap.put("*.js","anon");
filterChainDefinitionMap.put("*.css","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/easyui/**","anon");
filterChainDefinitionMap.put("/images/**","anon");
filterChainDefinitionMap.put("/media/**","anon");
filterChainDefinitionMap.put("/kaptcha","anon");
filterChainDefinitionMap.put("/wechat/**","anon");
filterChainDefinitionMap.put("/wechat/callback","anon");
filterChainDefinitionMap.put("/logout","logout"); //不登录也可以访问
//这个值之后从数据库中查询到【用户-角色-权限-资源】/wechat
//http://bugtracker.itsource.cn/wechat/callback
//filterChainDefinitionMap.put("/s/permission.jsp","perms[user:*]");
//filterChainDefinitionMap.put("/s/employee.jsp","perms[employee:*]");
//1.拿到数据,放入map中
List<Permission> premission =permissionService.findAll();
//2.便利权限,拿到权限和资源
for (Permission permission:premission){
String url = permission.getUrl();//资源
String sn = permission.getSn();//权限
//把路径和资源放到拦截其中
filterChainDefinitionMap.put(url,"yxbPerms["+sn+"]" );
}
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
}