概述
权限是大部分的后台管理系统都需要实现的功能,用户控制不同的角色能够进行的不同的操作。Spring Security的可以进行用户的角色权限控制,也可以进行用户的操作权限控制。在之前的代码实现上,我们仅仅只是实现用户的登录,在用户信息验证的时候使用UserDetailsService,但是却一直忽略了用户的权限。
一. 启动类配置
/**
* 开启方法的注解安全校验。
* securedEnabled @Secured("ROLE_abc") 该注解是Spring security提供的
* jsr250Enabled @RolesAllowed("admin") 该注解是 JSR250 支持的注解形式
* prePostEnabled @PreAuthorize("hasAuthority('user:add')
*/
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
二. 基于角色的权限控制
2.1 自定义认证信息类, 查询用户的密码和权限
@Component
public class UserSecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* 调用形式有两种:
* 1. 此时构建的 SimpleGrantedAuthority 必须是以 ROLE_ 开头, 例如 ROLE_admin, ROLE_manager.
* 实现全权限控制的时候使用 @RolesAllowed("ROLE_admin") 或者 @RolesAllowed("admin") 都可以
* 2. 此时构建的 SimpleGrantedAuthority 必须是以 ROLE_ 开头, 例如 ROLE_admin, ROLE_manager.
* 实现全权限控制的时候使用 @Secured("ROLE_admin") ROLE_是不能省略的。
*/
return new User(username, sysUser.getPassword(),
Arrays.asList(new SimpleGrantedAuthority("ROLE_admin")));
}
}
注意: 我们在构建SimpleGrantedAuthority对象的时候,用户的角色必须是以
ROLE_
开头,例如ROLE_admin
、ROLE_manager
2.2 角色权限控制使用
在控制器上进行用户访问控制的时候,基于角色有两种书写方式:
方式一:@RolesAllowed
/**
* @RolesAllowed 中的值可以写成 "admin", 例如 @RolesAllowed("admin")
* 推荐: @RolesAllowed 中的值还可以写成 "ROLE_admin",例如 @RolesAllowed("ROLE_admin")
*/
@RequestMapping
@RolesAllowed("admin")
public Object getAll() {
return Arrays.asList(new User(10, "张"), new User(20, "李四"));
}
方式二:@Secured
/**
* @Secured 中的值必须为 "ROLE_admin",例如 @Secured("ROLE_admin"),ROLE_不能省略
*/
@RequestMapping
@Secured("ROLE_admin")
public Object getAll() {
return Arrays.asList(new User(10, "张"), new User(20, "李四"));
}
三. 基于操作的权限控制
当然我们也可以使用基于操作的权限控制,这个功能稍显得有点累赘,因为在实际的项目开发过程中我们都是基于角色的权限控制。
3.1 自定义认证信息类, 查询用户的密码和权限
@Component
public class UserSecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* A. new SimpleGrantedAuthority("user:delete") @PreAuthorize("hasAnyAuthority('user:add', 'user:list')") 无法访问。
* B. new SimpleGrantedAuthority("user:add") @PreAuthorize("hasAnyAuthority('user:add', 'user:list')") 可以访问。
* C. Arrays.asList(new SimpleGrantedAuthority("user:add"), new SimpleGrantedAuthority("user:list"))
* @PreAuthorize("hasAuthority('user:add') and hasAuthority('user:list')") 可以访问
* D. new SimpleGrantedAuthority("ROLE_admin") 定义角色
* @PreAuthorize("hasRole('admin')") 可以访问
*/
return new User(username, sysUser.getPassword(),
Arrays.asList(new SimpleGrantedAuthority("user:list"),
new SimpleGrantedAuthority("user:add")
));
}
}
3.2 控制器访问控制(针对角色)
/**
* @PreAuthorize 中的值可以为 "ROLE_admin", "admin",
* 例如 @PreAuthorize("hasRole('admin')") 或者为
* @PreAuthorize("hasRole('ROLE_admin')")
*/
@RequestMapping
@PreAuthorize("hasRole('admin')")
public Object getAll() {
return Arrays.asList(new User(10, "张"), new User(20, "李四"));
}
3.3 控制器访问控制(针对操作)
@RequestMapping
// @PreAuthorize("hasAuthority('user:add') and hasAuthority('user:list')")
// @PreAuthorize("hasAuthority('user:add') or hasAuthority('user:list')")
@PreAuthorize("hasAnyAuthority('user:add', 'user:list')")
public Object getAll() {
return Arrays.asList(new User(10, "张"), new User(20, "李四"));
}
3.4 访问无权限处理
.and()
.exceptionHandling()
.accessDeniedHandler(customizeAccessDeniedHandler) //无权限访问处理, customizeAccessDeniedHandler为无权限处理操作类
.and()
四 .示例
4.1 用户权限表设计
sql脚本
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`permmit` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
BEGIN;
INSERT INTO `sys_permission` VALUES (1, '删除用户', 'user:delete');
INSERT INTO `sys_permission` VALUES (2, '展示用户', 'user');
INSERT INTO `sys_permission` VALUES (3, '添加用户', 'user:add');
INSERT INTO `sys_permission` VALUES (4, '编辑用户', 'user:edit');
INSERT INTO `sys_permission` VALUES (5, '导出用户', 'user:export');
INSERT INTO `sys_permission` VALUES (6, '部门展示', 'dept');
INSERT INTO `sys_permission` VALUES (7, '删除部门', 'dept:delete');
COMMIT;
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(20) NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_role` VALUES (1, 'admin', '系统管理员');
INSERT INTO `sys_role` VALUES (2, 'finance', '财务管理');
INSERT INTO `sys_role` VALUES (3, 'administration ', '行政管理');
COMMIT;
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`role_id` int(11) NOT NULL,
`permission_id` int(11) NOT NULL,
PRIMARY KEY (`role_id`,`permission_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
BEGIN;
INSERT INTO `sys_role_permission` VALUES (1, 1);
INSERT INTO `sys_role_permission` VALUES (1, 2);
INSERT INTO `sys_role_permission` VALUES (1, 3);
INSERT INTO `sys_role_permission` VALUES (1, 4);
INSERT INTO `sys_role_permission` VALUES (1, 5);
INSERT INTO `sys_role_permission` VALUES (1, 6);
INSERT INTO `sys_role_permission` VALUES (1, 7);
INSERT INTO `sys_role_permission` VALUES (2, 2);
INSERT INTO `sys_role_permission` VALUES (2, 5);
INSERT INTO `sys_role_permission` VALUES (2, 6);
INSERT INTO `sys_role_permission` VALUES (3, 2);
INSERT INTO `sys_role_permission` VALUES (3, 6);
COMMIT;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL,
`username` char(20) DEFAULT NULL,
`password` char(100) DEFAULT NULL,
`mobile` char(15) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` VALUES (1, 'jack', '$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m', '13268050688');
INSERT INTO `sys_user` VALUES (2, 'rose', '$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m', '13268050688');
COMMIT;
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`role_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2);
INSERT INTO `sys_user_role` VALUES (2, 3);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
4.2 service层
@Service
public class SysUserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public SysUser getSysUserByUsenameOrMobile(String usernameOrMobile) throws EmptyResultDataAccessException {
String sql = "select u.*, r.role_name, p.permmit from sys_user u join sys_user_role ur on u.id = ur.user_id " +
" join sys_role r on r.id = ur.role_id " +
" join sys_role_permission rp on r.id = rp.role_id " +
" join sys_permission p on rp.permission_id = p.id" +
" where u.username = ? or u.mobile = ? ";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, usernameOrMobile, usernameOrMobile);
SysUser sysUser = null;
if(null != list && list.size() > 0) {
sysUser = new SysUser();
Map<String, Object> map = list.get(0);
sysUser.setId((Integer)map.get("id"));
sysUser.setUsername((String)map.get("username"));
sysUser.setPassword((String)map.get("password"));
sysUser.setMobile((String)map.get("mobile"));
Set<String> roles = new HashSet<>();
Set<String> permissions = new HashSet<>();
// 是设置 角色
list.forEach(rows -> {
roles.add((String)rows.get("role_name"));
permissions.add((String) rows.get("permmit"));
});
sysUser.setRoles(roles);
sysUser.setPermissions(permissions);
}
return sysUser;
}
}
4.3 用户权限的查询
// 该类的作用是处理用户登录名和密码
@Component
public class UserSecurityService implements UserDetailsService {
private static Logger logger = LoggerFactory.getLogger(UserSecurityService.class);
@Resource
private SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("用户名或者电话:" + username);
SysUser sysUser = null;
try{
sysUser = sysUserService.getSysUserByUsenameOrMobile(username);
}catch (EmptyResultDataAccessException exception) { //没有对应的用户名异常
throw new UsernameNotFoundException("用户或密码错误");
}
if(null == sysUser) {
throw new UsernameNotFoundException("用户名或密码错误");
}else {
/**
* User第一参数是:用户名
* 第二个参数是:pssword, 是从数据库查出来的
* 第三个参数是: 权限
*/
User user = null;
try{
user = new User(username,
sysUser.getPassword(),
// getAuthorities(sysUser.getRoles()));
getMethodsAuthorities(sysUser.getPermissions()));
}catch (InternalAuthenticationServiceException exception) {
throw exception; // 在此处,将异常接着往外抛,抛给AuthenticationFailureHandler处理
}
return user;
}
}
// [new SimpleGrantedAuthority("user:list"), new SimpleGrantedAuthority("user:add")]
private List<SimpleGrantedAuthority> getMethodsAuthorities(Set<String> permissions) {
List<SimpleGrantedAuthority> list = new ArrayList<>();
if(null != permissions && permissions.size() > 0){
permissions.forEach(permission -> {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission); // user:list dept:list
list.add(simpleGrantedAuthority);
});
}
return list;
}
// [new SimpleGrantedAuthority("ROLE_admin"), new SimpleGrantedAuthority("ROLE_xxx")]
// 封装用户角色权限
private List<SimpleGrantedAuthority> getAuthorities(Set<String> roles) {
List<SimpleGrantedAuthority> list = new ArrayList<>();
if(null != roles && roles.size() > 0){
roles.forEach(role -> {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + role);
list.add(simpleGrantedAuthority);
});
}
return list;
}
}
4.4 Controller层
// @RolesAllowed("admin")
@RequestMapping("/user")
@Controller
public class UserController {
/**
* This annotation can be specified on a class or on method(s). Specifying it
* at a class level means that it applies to all the methods in the class.
* Specifying it on a method means that it is applicable to that method only.
*
* @RolesAllowed 可以写的值 ROLE_admin admin
*
* he <code>Secured</code> annotation is used to define a list of security configuration
* attributes for business methods.
* @Secured 只支持ROLE_admin 尽量用在service中。
*/
@RequestMapping
// @RolesAllowed("admin")
// @Secured({"ROLE_admin", "ROLE_finance"})
// @PreAuthorize("hasAuthority('user:list')")
public Object get() {
return "/index.html";
}
@ResponseBody
@RequestMapping("/list")
// @PreAuthorize("hasRole('ROLE_admin')")
//@RolesAllowed({"admin", "finance", "administration "})
// @PreAuthorize("hasRole('admin') or hasRole('finance')")
// @PreAuthorize("hasRole('admin') and hasRole('finance')") //表是同时拥有这两个角色才能访问
// @PreAuthorize("hasAnyRole('admin', 'finance')") //与上面的属性形式一样
// @PreAuthorize("hasAuthority('user:list')") //3个用户都有这个权限
public Object list() {
return "list";
}
@ResponseBody
@RequestMapping("/delete")
// @PreAuthorize("hasAuthority('user:delete')") //只有jack
public Object delete() {
return "delete";
}
@ResponseBody
@RequestMapping("/export")
// @PreAuthorize("hasAuthority('user:export')") // 只有rose, jack
public Object export() {
return "export";
}
}
作者:任未然
链接:https://www.jianshu.com/p/b4b498a2642b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:任未然
链接:https://www.jianshu.com/p/b4b498a2642b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:任未然
链接:https://www.jianshu.com/p/b4b498a2642b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:任未然
链接:https://www.jianshu.com/p/b4b498a2642b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来源:oschina
链接:https://my.oschina.net/lenglingx/blog/4311404