此博客用于个人学习,来源于ssm框架的书籍,对知识点进行一个整理。
第三章 SSM 权限操作:
1. 数据库与表结构:
1.1 用户表:
用户表信息描述 users:
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2 | 无意义,主键uuid |
2 | varchar2 | 非空,唯一 | |
3 | username | varchar2 | 用户名 |
4 | password | varchar2 | 密码(加密) |
5 | phoneNum | varchar2 | 电话 |
6 | status | int | 状态0 未开启 1 开启 |
sql 语句:
CREATE TABLE users(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
email VARCHAR2(50) UNIQUE NOT NULL,
username VARCHAR2(50),
PASSWORD VARCHAR2(50),
phoneNum VARCHAR2(20),
STATUS INT
)
实体类 UserInfo:
public class UserInfo {
private String id;
private String username;
private String email;
private String password;
private String phoneNum;
private int status;
private String statusStr;
private List<Role> roles;
//其他get/set方法
}
1.2 角色表:
角色表信息描述 role:
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2 | 无意义,主键uuid |
2 | roleName | varchar2 | 角色名 |
3 | roleDesc | varchar2 | 角色描述 |
sql 语句:
CREATE TABLE role(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
roleName VARCHAR2(50) ,
roleDesc VARCHAR2(50)
)
实体类 Role:
public class Role {
private String id;
private String roleName;
private String roleDesc;
private List<Permission> permissions;
private List<User> users;
//其他get/set方法
}
1.3 中间表 users_role:
用户与角色之间是多对多关系,我们通过 user_role 表来描述其关联,在实体类中User中存在 List,在 Role 中有 List:
CREATE TABLE users_role(
userId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(userId,roleId),
FOREIGN KEY (userId) REFERENCES users(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
1.4 资源权限表:
权限资源表描述 permission:
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2 | 无意义,主键uuid |
2 | permissionName | varchar2 | 权限名 |
3 | url | varchar2 | 资源路径 |
sql 语句:
CREATE TABLE permission(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
permissionName VARCHAR2(50) ,
url VARCHAR2(50)
)
实体类 :
public class Permission {
private String id;
private String permissionName;
private String url;
private List<Role> roles;
//其他get/set方法
}
1.5 中间表 role_permission:
权限资源与角色是多对多关系,我们使用 role_permission 表来描述。在实体类 Permission 中存在 List ,在 Role 类中有 List。
CREATE TABLE role_permission(
permissionId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(permissionId,roleId),
FOREIGN KEY (permissionId) REFERENCES permission(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
2. 用户管理:
2.1 用户登录:
在 web 层的资源包下创建 spring-security.xml 文件,配置 spring security 。
<!-- 切换成数据库中的用户名和密码 -->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<!-- 配置加密的方式
<security:password-encoder ref="passwordEncoder"/>-->
</security:authentication-provider>
</security:authentication-manager>
创建登录页面 login.jsp。
Service:先定义接口 IUserService,继承 UserDetailsService,然后定义它的实现类 IUserServiceImpl,注入 IUserDao,实现方法 loadUserByUsername。
public interface IUserService extends UserDetailsService {
}
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = null;
try {
userInfo = userDao.findByUsername(username);
} catch (Exception e) {
e.printStackTrace();
}
//处理自己的用户对象封装成UserDetails
// User user=new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthority(userInfo.getRoles()));
User user = new User(userInfo.getUsername(), "{noop}" + userInfo.getPassword(), userInfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userInfo.getRoles()));
return user;
}
//作用就是返回一个List集合,集合中装入的是角色描述
public List<SimpleGrantedAuthority> getAuthority(List<Role> roles) {
List<SimpleGrantedAuthority> list = new ArrayList<>();
for (Role role : roles) {
list.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
}
return list;
}
}
Dao:定义接口 IUserDao 接口,添加方法 findByUsername:
public interface IUserDao {
@Select("select * from users where username=#{username}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "email", column = "email"),
@Result(property = "password", column = "password"),
@Result(property = "phoneNum", column = "phoneNum"),
@Result(property = "status", column = "status"),
@Result(property = "roles",column = "id",javaType = java.util.List.class,many = @Many(select = "com.itheima.ssm.dao.IRoleDao.findRoleByUserId"))
})
public UserInfo findByUsername(String username) throws Exception;
}
2.2 用户退出:
在 spring-security.xml 文件中进行配置:
<!-- 退出 -->
<security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
在 login.jsp 中添加注销的链接:
<a href="${pageContext.request.contextPath}/logout.do" class="btn btn-default btn-flat">注销</a>
2.3 用户查询:
创建用户查询页面 user-list.jsp。
定义 UserController 类,添加 findAll 方法,然后在 service 和 dao 中添加方法。
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<UserInfo> userList = userService.findAll();
mv.addObject("userList", userList);
mv.setViewName("user-list");
return mv;
}
}
public interface IUserService extends UserDetailsService {
List<UserInfo> findAll() throws Exception;
}
public interface IUserDao {
@Select("select * from users")
List<UserInfo> findAll() throws Exception;
}
2.4 用户添加:
创建用户添加页面 user-add.jsp。
Controller:在 UserController 类添加 save 方法,注入 service。
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
//用户添加
@RequestMapping("/save.do")
public String save(UserInfo userInfo) throws Exception {
userService.save(userInfo);
return "redirect:findAll.do";
}
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<UserInfo> userList = userService.findAll();
mv.addObject("userList", userList);
mv.setViewName("user-list");
return mv;
}
}
Service:定义 UserService 接口及其实现类,注入 UserDao,注入 BCryptPasswordEncoder 类用于加密。
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public UserInfo findById(String id) throws Exception{
return userDao.findById(id);
}
@Override
public void save(UserInfo userInfo) throws Exception {
//对密码进行加密处理
userInfo.setPassword(bCryptPasswordEncoder.encode(userInfo.getPassword()));
userDao.save(userInfo);
}
}
在 spring-security.xml 类中配置加密类。
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
Dao:在 IUserDao 接口中添加 save 方法。
public interface IUserDao {
@Insert("insert into users(email,username,password,phoneNum,status) values(#{email},#{username},#{password},#{phoneNum},#{status})")
void save(UserInfo userInfo) throws Exception;
}
2.5 用户详情:
创建用户详情页面 user-show.jsp。
Controller:定义 UserController 类,注入 UserService,添加 findById 方法。
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
//查询指定id的用户
@RequestMapping("/findById.do")
public ModelAndView findById(String id) throws Exception{
ModelAndView mv = new ModelAndView();
UserInfo userInfo = userService.findById(id);
mv.addObject("user",userInfo);
mv.setViewName("user-show1");
return mv;
}
}
Dao:在 IUserDao 接口中添加 findById 方法,关联查询出 role 的信息。
public interface IUserDao {
@Select("select * from users where id=#{id}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "email", column = "email"),
@Result(property = "password", column = "password"),
@Result(property = "phoneNum", column = "phoneNum"),
@Result(property = "status", column = "status"),
@Result(property = "roles",column = "id",javaType = java.util.List.class,many = @Many(select = "com.itheima.ssm.dao.IRoleDao.findRoleByUserId"))
})
UserInfo findById(String id) throws Exception;
}
由于此时用户表与角色表是多对多的关系,于是在 IRoleDao 接口中也需要添加 findRoleByUserId 方法,关联查询出 permission 的信息。
public interface IRoleDao {
//根据用户id查询出所有对应的角色
@Select("select * from role where id in (select roleId from users_role where userId=#{userId})")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "roleName", column = "roleName"),
@Result(property = "roleDesc", column = "roleDesc"),
@Result(property = "permissions",column = "id",javaType = java.util.List.class,many = @Many(select = "com.itheima.ssm.dao.IPermissionDao.findPermissionByRoleId"))
})
public List<Role> findRoleByUserId(String userId) throws Exception;
}
由于此时角色表与资源权限表是多对多的关系,于是在 IPermissionDao 接口中也需要添加 findByRoleId 方法。
public interface IPermissionDao {
@Select("select * from permission where id in (select permissionId from role_permission where roleId=#{id} )")
public List<Permission> findPermissionByRoleId(String id) throws Exception;
}
3. 角色管理:
3.1 角色查询:
创建角色信息展示页面 role-list.jsp。
Controller:定义 RoleController 类,注入 roleService 类,添加 findAll 方法。
@RequestMapping("/role")
@Controller
public class RoleController {
@Autowired
private IRoleService roleService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Role> roleList = roleService.findAll();
mv.addObject("roleList", roleList);
mv.setViewName("role-list");
return mv;
}
}
Dao:在 IRoleDao 中添加 findAll 方法。
public interface IUserDao {
@Select("select * from users")
List<UserInfo> findAll() throws Exception;
}
3.2 角色添加:
创建角色添加页面 role-add.jsp。
Controller:在 RoleController 类中添加 save 方法。
@RequestMapping("/role")
@Controller
public class RoleController {
@Autowired
private IRoleService roleService;
@RequestMapping("/save.do")
public String save(Role role) throws Exception {
roleService.save(role);
return "redirect:findAll.do";
}
}
Dao:在 IRoleDao 中添加 save 方法。
public interface IRoleDao {
@Insert("insert into role(roleName,roleDesc) values(#{roleName},#{roleDesc})")
void save(Role role);
}
4. 资源权限管理:
4.1 资源权限查询:
创建权限资源页面 permission-list.jsp。
Controller:定义 PermissionController 类,注入 permissionService 类,添加 findAll 方法。
@Controller
@RequestMapping("/permission")
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv=new ModelAndView();
List<Permission> permissionList = permissionService.findAll();
mv.addObject("permissionList",permissionList);
mv.setViewName("permission-list");
return mv;
}
}
Dao:在 IPermissionDao 中添加 findAll 方法。
public interface IPermissionDao {
@Select("select * from permission")
List<Permission> findAll() throws Exception;
}
4.2 资源权限添加:
创建权限资源添加页面 permission-add.jsp。
Controller:在 PermissionController 类中添加 save 方法。
@Controller
@RequestMapping("/permission")
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@RequestMapping("/save.do")
public String save(Permission permission) throws Exception {
permissionService.save(permission);
return "redirect:findAll.do";
}
}
Dao:在 IPermissionDao 中添加 save 方法。
public interface IPermissionDao {
@Insert("insert into permission(permissionName,url) values(#{permissionName},#{url})")
void save(Permission permission) throws Exception;
}
5. 权限关联与控制:
5.1 用户角色关联:
用户与角色之间是多对多关系,我们要建立它们之间的关系,只需要在中间表user_role插入数据即可。
在 user-list.jsp 页面上添加链接用于添加角色。
<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加角色</a>
创建用于添加角色的页面 user-roe-add.jsp。
Controller:在 UserController 中,添加 findUserByIdAndAllRole 方法,用于查询用户以及用户可以添加的角色;添加 addRoleToUser 方法,用于用户添加角色。
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
//给用户添加角色
@RequestMapping("/addRoleToUser.do")
public String addRoleToUser(@RequestParam(name = "userId", required = true) String userId, @RequestParam(name = "ids", required = true) String[] roleIds) {
userService.addRoleToUser(userId, roleIds);
return "redirect:findAll.do";
}
//查询用户以及用户可以添加的角色
@RequestMapping("/findUserByIdAndAllRole.do")
public ModelAndView findUserByIdAndAllRole(@RequestParam(name = "id", required = true) String userid) throws Exception {
ModelAndView mv = new ModelAndView();
//1.根据用户id查询用户
UserInfo userInfo = userService.findById(userid);
//2.根据用户id查询可以添加的角色
List<Role> otherRoles = userService.findOtherRoles(userid);
mv.addObject("user", userInfo);
mv.addObject("roleList", otherRoles);
mv.setViewName("user-role-add");
return mv;
}
}
Service:在 IUserService 以及实现类中添加 addRoleToUser 和 findOtherRoles 方法。
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public void addRoleToUser(String userId, String[] roleIds) {
for(String roleId:roleIds){
userDao.addRoleToUser(userId,roleId);
}
}
@Override
public List<Role> findOtherRoles(String userId) {
return userDao.findOtherRoles(userId);
}
}
Dao:在 IUserDao 接口中添加 findOtherRoles 和 addRoleToUser 方法。
public interface IUserDao {
//用于查找可以添加的角色
@Select("select * from role where id not in (select roleId from users_role where userId=#{userId})")
List<Role> findOtherRoles(String userId);
//用于添加用户与角色关系
@Insert("insert into users_role(userId,roleId) values(#{userId},#{roleId})")
void addRoleToUser(@Param("userId") String userId, @Param("roleId") String roleId);
}
5.2 角色权限关联:
角色与权限之间是多对多关系,我们要建立它们之间的关系,只需要在中间表role_permission插入数据即可。
在 role-list.jsp 页面上添加链接用于添加权限。
<a href="${pageContext.request.contextPath}/role/findRoleByIdAndAllPermission.do? id=${role.id}" class="btn bg-olive btn-xs">添加权限</a>
创建用于添加权限的页面 role-permission-add.jsp。
Controller:在 RoleController 中,添加 findRoleByIdAndAllPermission 方法,用于查询角色以及角色可以添加的权限;添加 addPermissionToRole 方法,用于角色添加权限。
@RequestMapping("/role")
@Controller
public class RoleController {
@Autowired
private IRoleService roleService;
//给角色添加权限
@RequestMapping("/addPermissionToRole.do")
public String addPermissionToRole(@RequestParam(name = "roleId", required = true) String roleId, @RequestParam(name = "ids", required = true) String[] permissionIds) throws Exception {
roleService.addPermissionToRole(roleId, permissionIds);
return "redirect:findAll.do";
}
//根据roleId查询role,并查询出可以添加的权限
@RequestMapping("/findRoleByIdAndAllPermission.do")
public ModelAndView findRoleByIdAndAllPermission(@RequestParam(name = "id", required = true) String roleId) throws Exception {
ModelAndView mv = new ModelAndView();
//根据roleId查询role
Role role = roleService.findById(roleId);
//根据roleId查询可以添加的权限
List<Permission> otherPermissions = roleService.findOtherPermissions(roleId);
mv.addObject("role", role);
mv.addObject("permissionList", otherPermissions);
mv.setViewName("role-permission-add");
return mv;
}
}
Service:在 IUserService 以及实现类中添加 addPermissionToRole 和 findOtherPermissions 方法。
@Service
@Transactional
public class RoleServiceImpl implements IRoleService {
@Autowired
private IRoleDao roleDao;
@Override
public void addPermissionToRole(String roleId, String[] permissionIds) {
for(String permissionId:permissionIds){
roleDao.addPermissionToRole(roleId,permissionId);
}
}
@Override
public List<Permission> findOtherPermissions(String roleId) {
return roleDao.findOtherPermissions(roleId);
}
}
Dao:在 IRoleDao 接口中添加 findOtherPermissions 和 addPermissionToRole 方法。
public interface IRoleDao {
@Select("select * from permission where id not in (select permissionId from role_permission where roleId=#{roleId})")
List<Permission> findOtherPermissions(String roleId);
@Insert("insert into role_permission(roleId,permissionId) values(#{roleId},#{permissionId})")
void addPermissionToRole(@Param("roleId") String roleId, @Param("permissionId") String permissionId);
}
5.3 服务器端方法级权限控制:
有几种注解可以使用,这次使用支持表达式的注解 @PreAuthorize。
在 spring-security.xml 中将其开启。
<security:global-method-security pre-post-annotations="enabled" jsr250-annotations="enabled" secured-annotations="enabled"></security:global-method-security>
在 UserController 类中的 save 和 findAll 方法上添加此注解,用于对用户的某些权限操作做一个限制。
//用户添加
@RequestMapping("/save.do")
@PreAuthorize("authentication.principal.username == 'tom'")
public String save(UserInfo userInfo) throws Exception {
userService.save(userInfo);
return "redirect:findAll.do";
}
@RequestMapping("/findAll.do")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<UserInfo> userList = userService.findAll();
mv.addObject("userList", userList);
mv.setViewName("user-list");
return mv;
}
在 aside.jsp 上对 “用户管理” 的标签上添加 <security:authorize access=“hasRole(‘ADMIN’)”> 包围,防止没有操作用户权限的用户页面上有此项功能。
<ul class="treeview-menu">
<li id="system-setting">
<security:authorize access="hasRole('ADMIN')">
<a href="${pageContext.request.contextPath}/user/findAll.do">
<i class="fa fa-circle-o"></i> 用户管理
</a>
</security:authorize>
</li>
<li id="system-setting">
<a href="${pageContext.request.contextPath}/role/findAll.do">
<i class="fa fa-circle-o"></i> 角色管理
</a>
</li>
<li id="system-setting">
<a href="${pageContext.request.contextPath}/permission/findAll.do">
<i class="fa fa-circle-o"></i> 资源权限管理
</a>
</li>
<li id="system-setting">
<a href="${pageContext.request.contextPath}/sysLog/findAll.do">
<i class="fa fa-circle-o"></i> 访问日志
</a>
</li>
</ul>