spirng框架之spring security(二)使用access()方法实现RBAC权限模型(另包涵简单动态菜单实现)

提示:spirng框架之spring security(二)insert 语句补充 文章中有 数据添加语句,可拿来即用。

文章目录


一、RBAC权限模型(表结构)

        RBAC权限模型:基于角色的访问控制。

        用户具有角色,角色拥有资源权限:表结构如下:

-- 用户表
create sequence SEQ_SYS_USER
minvalue 1
maxvalue 999999999999999
start with 1
increment by 1
nocache
order;

create table SYS_USER
(
  ID                    NUMBER(15) PRIMARY KEY, 
  USERNAME              VARCHAR2(50) not null,
  PASSWORD              VARCHAR2(200) not null,
  ADDTIME               DATE not null,
  ACCOUNTNONEXPIRED     NUMBER(1) not null, --账户是否过期
  ACCOUNTNONLOCKED      NUMBER(1) not null, --账户是否锁定
  CREDENTIALSNONEXPIRED NUMBER(1) not null, --密码是否过去
  ENABLED               NUMBER(1) not null  --账户是否可用
)


-- 资源权限表
create sequence SEQ_SYS_PERMISSION
minvalue 1
maxvalue 999999999999999
start with 1
increment by 1
nocache
order;

create table SYS_PERMISSION
(
  ID                    NUMBER(15) PRIMARY KEY,
  AVAILABLE             NUMBER(1) not null,
  NAME                  VARCHAR2(30) not null, --名称
  PARENT_ID             NUMBER(15) not null, --父ID
  PARENT_IDS            NUMBER(15) not null,
  PERMISSION            VARCHAR2(50) not null,
  RESOURCE_TYPE         VARCHAR2(50) not null, --类型:菜单、请求、按钮
  url                   VARCHAR2(100) not null --请求的URL
)


-- 角色表
create sequence SEQ_SYS_ROLE
minvalue 1
maxvalue 999999999999999
start with 1
increment by 1
nocache
order;

create table SYS_ROLE
(
  ID                    NUMBER(15) PRIMARY KEY,
  AVAILABLE             NUMBER(1) not null,
  DESCRIPTION            VARCHAR2(50) not null,
  ROLE                  VARCHAR2(50) not null
)


-- 角色、资源关联表,关联角色和资源权限
create table SYS_ROLE_PERMISSION
(
  ROLE_ID             NUMBER(15) not null,
  PERMISSION_ID       NUMBER(15) not null
)


-- 用户、角色关联表,关联用户和角色
create table SYS_USER_ROLE
(
  USER_ID             NUMBER(15) not null,
  ROLE_ID       NUMBER(15) not null
)

 

二、开发步骤

1. 生成数据表实体类

用户表SYS_USER实体类(重点注意@ManyToMany关联角色表)

package com.securitydemo.entity;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.*;

/**
 * 用户领域实体bean数据表映射
 */
@Entity
@Table(name = "SYS_USER", schema = "PRODUCTION", catalog = "")
public class User implements UserDetails {
    private long id;
    private String password;
    private String username;
    private Date addtime;

    //用户关联到角色,RBAC权限模型
    private Set<SysRole> roles = new HashSet<SysRole>();

    //实现UserDetails接口重写的属性或方法
    //private Set<GrantedAuthority> authorities;//角色
    private Boolean accountNonExpired;//账户没有过期
    private Boolean accountNonLocked;//账户没有锁定
    private Boolean credentialsNonExpired;//密码没有过期
    private Boolean enabled;//账户可用


    @SequenceGenerator(name = "generator", sequenceName = "SEQ_A_TBL", allocationSize = 1)
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
    @Column(name = "ID", unique = true, nullable = false, scale = 0)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Basic
    @Column(name = "PASSWORD")
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Basic
    @Column(name = "USERNAME")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Basic
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "ADDTIME")
    public Date getAddtime() {
        return addtime;
    }

    public void setAddtime(Date addtime) {
        this.addtime = addtime;
    }

    @ManyToMany(targetEntity = SysRole.class, fetch = FetchType.EAGER)
    @JoinTable(name = "SYS_USER_ROLE", joinColumns = {@JoinColumn(name = "USER_ID")}, inverseJoinColumns = {@JoinColumn(name = "ROLE_ID")})
    public Set<SysRole> getRoles() {
        return roles;
    }

    public void setRoles(Set<SysRole> roles) {
        this.roles = roles;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User that = (User) o;

        if (id != that.id) return false;
        if (password != null ? !password.equals(that.password) : that.password != null) return false;
        if (username != null ? !username.equals(that.username) : that.username != null) return false;
        if (addtime != null ? !addtime.equals(that.addtime) : that.addtime != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (id ^ (id >>> 32));
        result = 31 * result + (password != null ? password.hashCode() : 0);
        result = 31 * result + (username != null ? username.hashCode() : 0);
        result = 31 * result + (addtime != null ? addtime.hashCode() : 0);
        return result;
    }


    //实现UserDetails接口重写的属性或方法
    @Transient  //@Transient注释意思是不会被Spring Data JPA框架序列化到数据库,单纯的作为一个临时字段,接收完数据后就暂且用不上了
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }

//    public void setAuthorities(Set<GrantedAuthority> authorities) {
//        this.authorities = authorities;
//    }

    @Column(name = "ACCOUNTNONEXPIRED")
    public Boolean getAccountNonExpired() {
        return accountNonExpired;
    }

    public void setAccountNonExpired(Boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    @Column(name = "ACCOUNTNONLOCKED")
    public Boolean getAccountNonLocked() {
        return accountNonLocked;
    }

    public void setAccountNonLocked(Boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    @Column(name = "CREDENTIALSNONEXPIRED")
    public Boolean getCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    @Column(name = "ENABLED")
    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    @Transient
    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Transient
    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Transient
    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Transient
    @Override
    public boolean isEnabled() {
        return enabled;
    }
}

角色表SYS_ROLE实体类(重点注意@ManyToMany关联到URL资源权限)

package com.securitydemo.entity;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
 * 角色实体类,实现RBAC权限模型用
 */
@Entity
@Table(name = "SYS_ROLE", schema = "PRODUCTION", catalog = "")
public class SysRole {
    private long id;
    private Boolean availbale;
    private String description;
    private String role;

    //角色关联到URL资源权限,RBAC权限模型
    private Set<SysPermission> Permissions = new HashSet<SysPermission>();

    @SequenceGenerator(name = "generator", sequenceName = "SEQ_SYS_ROLE", allocationSize = 1)
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
    @Column(name = "ID", unique = true, nullable = false, scale = 0)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "AVAILABLE")
    public Boolean getAvailbale() {
        return availbale;
    }

    public void setAvailbale(Boolean availbale) {
        this.availbale = availbale;
    }

    @Column(name = "DESCRIPTION")
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Column(name = "ROLE")
    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    @ManyToMany(targetEntity = SysPermission.class, fetch = FetchType.EAGER)
    @JoinTable(name = "SYS_ROLE_PERMISSION", joinColumns = {@JoinColumn(name = "ROLE_ID")}, inverseJoinColumns = {@JoinColumn(name = "PERMISSION_ID")})
    public Set<SysPermission> getPermissions() {
        return Permissions;
    }

    public void setPermissions(Set<SysPermission> permissions) {
        Permissions = permissions;
    }
}

资源权限表SYS_PERMISSION实体类

package com.securitydemo.entity;

import javax.persistence.*;

/**
 * 权限实体类,实现RBAC权限模型用
 */
@Entity
@Table(name = "SYS_PERMISSION", schema = "PRODUCTION", catalog = "")
public class SysPermission {
    private long id;
    private Boolean availbale;
    private String name;
    private Long parentId;
    private Long parentIds;
    private String permission;
    private String resourceType;
    private String url;

    @SequenceGenerator(name = "generator", sequenceName = "SEQ_SYS_PERMISSION", allocationSize = 1)
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "AVAILABLE")
    public Boolean getAvailbale() {
        return availbale;
    }

    public void setAvailbale(Boolean availbale) {
        this.availbale = availbale;
    }

    @Column(name = "NAME")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Column(name = "PARENT_ID")
    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    @Column(name = "PARENT_IDS")
    public Long getParentIds() {
        return parentIds;
    }

    public void setParentIds(Long parentIds) {
        this.parentIds = parentIds;
    }

    @Column(name = "PERMISSION")
    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    @Column(name = "RESOURCE_TYPE")
    public String getResourceType() {
        return resourceType;
    }

    public void setResourceType(String resourceType) {
        this.resourceType = resourceType;
    }

    @Column(name = "URL")
    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

2. 定义JPA数据层访问接口(MyUserRepository)

package com.securitydemo.repository;

import com.securitydemo.entity.User;
import org.springframework.data.repository.CrudRepository;

/**
 * spring data JPA继承CrudRepository接口,用于Spring Security登录用户认证查询
 */
public interface MyUserRepository extends CrudRepository<User, Long> {
    /**
     * 方法命名查询
     * @param username 用户名
     * @return
     */
    User findByUsername(String username);

}

3. 实现登录认证用户详情类(MyUserDetailsService)

package com.securitydemo.service.impl;

import com.securitydemo.entity.User;
import com.securitydemo.repository.MyUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 自定义创建用户详情服务,实现security提供的UserDetailsService接口
 */
@Service
public class MyUserDetailsService implements UserDetailsService {

    /**
     * 注入UserRepository获取数据库用户信息
     */
    private MyUserRepository myUserRepo;

    /**
     * MyUserDetailsService类通过构造器将MyUserRepository注入进来
     * @param userRepo
     */
    @Autowired
    public MyUserDetailsService(MyUserRepository userRepo){
        this.myUserRepo=userRepo;
    }

    /**
     * 用户登录时,security会调用loadUserByUsername方法去数据库查询数据
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        System.out.println("username==="+username);
        //根据页面登录的用户名查询数据库用户信息进行身份认证start
        /**
         * 一.
         * 此处User对象是com.securitydemo.entity.User自定义实体领域,其中实现了UserDetails接口。
         * 自定义User实体领域实现UserDetails接口可以扩展除UserDetails接口中几个属性以外的许多其他属性,如用户的添加时间,备注,手机号等属性。
         *
         * 二.
         * 如果自定义User实体领域不继承或者实现UserDetails接口,也可以通过用户名查询出用户信息后填装到org.springframework.security.core.userdetails.User
         * 默认提供的User对象中返回。
         * 不管怎样返回User对象,最终都要返回一个UserDetails。
         */
        User user=myUserRepo.findByUsername(username);
        System.out.println("user-====="+user);
        //判断
        if(user!=null){
            System.out.println(user.getUsername()+"---====---"+user.getPassword()+"----"+user.getAddtime());
            return user;
        }
        throw new UsernameNotFoundException(username+"用户名不存在!");
        //根据页面登录的用户名查询数据库用户信息进行身份认证end

        /**
         * 二. 通过用户名查询出用户信息后填装到org.springframework.security.core.userdetails.User默认提供的User对象中返回。
         */
//        //手动设置了权限及角色,也可以通过数据库查询获取
//        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("addUser,findAll,ADMIN,USER");  //配置权限及角色
//        //根据页面登录的用户名查询数据库用户信息进行身份认证
//        return new User(atblUserLv.getUsername(),atblUserLv.getPassword(),auths);
    }
}

4. 定义RBAC权限模型示例,Service接口

package com.securitydemo.service;

import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;

/**
 * RBAC权限模型示例,Service类
 */
public interface RbacService {

    /**
     * 定义一个方法,根据请求对象,判断其是否有权限访问某些资源
     * @param request 请求信息
     * @param authentication 登录用户信息
     * @return
     */
    public boolean hasPermission(HttpServletRequest request, Authentication authentication);
}

5. 定义RBAC权限模型示例,Service接口的实现类

package com.securitydemo.service.impl;

import com.securitydemo.entity.SysPermission;
import com.securitydemo.entity.SysRole;
import com.securitydemo.entity.User;
import com.securitydemo.repository.MyUserRepository;
import com.securitydemo.service.RbacService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;

/**
 * RBAC权限模型示例,Service实现类
 */
@Component("rbacService")
public class RbacServiceImpl implements RbacService {
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private MyUserRepository myUserRepository;

    /**
     * 编写RbacService实现类,判断URL是否在权限表中
     * @param request 请求信息
     * @param authentication 登录用户信息
     * @return
     */
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        System.out.println("主体principal:"+principal);
        boolean hasPermission =false;
        if(principal!=null && principal instanceof UserDetails){
            //获取登录的用户名
//            String username = ((UserDetails) principal).getUsername();
//            System.out.println("主体principal-username:"+username);
//            //TODO:从数据库读取当前用户所拥有权限的url,放入set集合中
//            User user=myUserRepository.findByUsername(username);
            /**
             * 此处可以查询数据库获取用户、权限、资源。也可以从登录认证主体中获得用户关联出权限、资源
             * 因为表实体中配置了关联关系,在用户登录证人的详情服务类中已经查询出用户详情存在于登录主体(principal)中,所以可以直接获取
             */
            User user=((User) principal);
            Set<String> urls = new HashSet<>();
            for(SysRole role : user.getRoles()){
                System.out.println("user.getRoles():"+role);
                for(SysPermission permission : role.getPermissions()){
                    System.out.println("permission.getUrl():"+permission.getUrl());
                    urls.add(permission.getUrl());
                }
            }

            //判断当前请求url,是否在拥有权限的url列表中(这里用antPathMatcher)
            for(String url:urls){
                System.out.println("授权url:"+url+"当前请求的URL:"+request.getRequestURI());
                if(antPathMatcher.match(url,request.getRequestURI())){
                    hasPermission = true;
                    break;
                }
            }
        }
        System.out.println("hasPermission:"+hasPermission);
        return hasPermission;
    }
}

6. 定义SecurityConfig类中access()方法自定义权限规则

    access():如果给定的SpEL表达式计算结果为true,就允许访问
    此例子中使用@Component("rbacService")自定义了一个组件bean类。实现了方法hasPermission判断请求的URL是否匹配上数据库中记录的URL,如果匹配上就返回true,否则返回false。
    SecurityConfig类中使用access()方法装配rbacService接口中的hasPermission()方法实现URL请求权限的判断。主要代码:                
.antMatchers("/","/logins","/test/hiRole").permitAll()
.anyRequest().access("@rbacService.hasPermission(request,authentication)")

package com.securitydemo.Config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;

/**
 * 自定义用户名密码
 * 方式一:通通过配置类进行配置实现身份认证登录(基于内存的用户存储)
 * 方式二:自定义实现类实现身份认证登录(基于数据库的用户存储)
 */
@Configuration //表明是一个配置类bean
@EnableWebSecurity //此注解1: 加载了WebSecurityConfiguration配置类, 配置安全认证策略。2: 加载了AuthenticationConfiguration, 配置了认证信息。
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 方式一:通过配置类进行配置实现身份认证登录(基于内存的用户存储)
     * @param auth
     * @throws Exception
     */
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        // 创建密码解析器
//        BCryptPasswordEncoder pe =new BCryptPasswordEncoder();
//        // 对密码进行加密
//        String password = pe.encode("a123456");
//        auth.inMemoryAuthentication()
//                .passwordEncoder(pe)  //默认没有,需要手动设置BCryptPasswordEncoder
//                .withUser("user01")
//                .password(password)
//                .roles("admin")
//                .and().withUser("user02").password(password).roles("user");
//
//    }

    /**
     * 方式二:自定义实现类实现身份认证登录,实现类MyUserDetailsService根据页面登录的用户名从数据库查询用户进行判断
     */
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("SecurityConfig run...configure(AuthenticationManagerBuilder auth)");
        /**
         * 将自动装配到SecurityConfig中的UserDetailsService实例传递进去
         * 也就是将从数据库查询到的用户详情传递进去
         */
        auth.userDetailsService(userDetailsService);//.passwordEncoder(encoder());
    }

    /**
     * 指定装备一个转码器使用注解@Bean
     * @return
     */
    @Bean
    public PasswordEncoder encoder(){
       return new BCryptPasswordEncoder();//进行转码 //NoOpPasswordEncoder.getInstance(); 不进行转码
    }

    /**
     * 方式二:自定义实现类实现身份认证登录,继续添加自定义登录页面与权限控制代码
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("SecurityConfig run...configure(HttpSecurity http)");

        //配置没有权限访问跳转自定义页面
       //http.exceptionHandling().accessDeniedPage("/error.html");
        //添加退出的映射地址
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/").permitAll();

        http.authorizeRequests()
                //.antMatchers("/test/adduser","/test/findAllUser","/test/hello").hasRole("USER") //具备指定权限的用户且认证通过才能访问指定请求。hasRole方法中默认加有"ROLE_"前缀
                //.antMatchers("/","/**").permitAll() //其他请求允许所有用户访问,不需要认证 "/**"
                .antMatchers("/","/logins","/test/hiRole").permitAll() //设置那些路径可以直接访问,不需要认证
                .anyRequest().access("@rbacService.hasPermission(request,authentication)")
                .and().formLogin().loginPage("/logins") //登陆页面设置
                .loginProcessingUrl("/user/login") //登陆访问路径,监听此路径来处理登录信息的提交
                .defaultSuccessUrl("/test/findMenu") //默认登录成功后跳转路径
                .and().csrf().disable()
        ;

//        http.formLogin()   //自定义自己编写的登陆页面
//                .loginPage("/logins.html")  //登陆页面设置
//                .loginProcessingUrl("/user/login")  //登陆访问路径
//                .defaultSuccessUrl("/test/hello").permitAll() //登陆成功后跳转路径
//                .and().authorizeRequests() //授权
//                .antMatchers("/","/user/login").permitAll() //设置那些路径可以直接访问,不需要认证
//                .antMatchers("/test/adduser").hasAuthority("addUser") //当前用户只有具有addUser权限时才能访问该路径,需要在启动类或配置类中开启基于方法的安全认证机制
//                .antMatchers("/test/findAllUser").hasAnyAuthority("addUser,findAll")//具备其中任意一个权限
                .antMatchers("/test/hiRole").hasRole("admin")
                .antMatchers("/test/hiAnyRole").hasAnyRole("admin,user")
//                .anyRequest().authenticated() //任何请求都必须经过身份验证
//                .and().csrf().disable() ;  //关闭csrf的保护
    }

}

到此,使用access()方法实现RBAC权限模型的核心代码4、5、6步的代码已经完成。

7. 实现Controller类

        请注意findMenu()方法,登录认证成功并且URL权限访问验证通过后访问此方法获得用户的系统菜单。

package com.securitydemo.controller;

import com.securitydemo.entity.*;
import com.securitydemo.repository.MyUserRepository;
import com.securitydemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * 测试用控制器(Controller)
 */
@Controller //这个注解可返回显示视图页面。@RestController注解相当于@ResponseBody + @Controller合在一起的作用,方法返回字符串值
@RequestMapping("/test")
public class TestController {

    @Resource
    private UserService userService;
    @Autowired
    private MyUserRepository myUserRepository;

    @GetMapping("/hello")
    @ResponseBody //这个注解此方法表示返回字符串内容hello security
    public String hello(@AuthenticationPrincipal User user){
        System.out.println("了解登录用户是谁:"+user); //使用AuthenticationPrincipal注解,User是用户详情服务中的用户对象
        if(user!=null){
            System.out.println("了解登录用户是谁:"+user.getUsername()+"||"+user.getPassword()+"||"+user.getEnabled());
        }
        System.out.println("-----hello----");
        return "hello security";
    }

    @GetMapping("/adduser")
    public String addUser(@AuthenticationPrincipal User user){
        System.out.println("了解登录用户是谁:"+user); //使用AuthenticationPrincipal注解,User是用户详情服务中的用户对象
        if(user!=null){
            System.out.println("了解登录用户是谁:"+user.getUsername()+"||"+user.getPassword()+"||"+user.getEnabled());
        }
        System.out.println("-----添加用户----");
        AtblUserLv atblUserLv=new AtblUserLv();
        atblUserLv.setUsername("吕亮");
        atblUserLv.setPassword("12345");
        atblUserLv.setAddtime(new Date());
        userService.insertUser(atblUserLv);
        return "success";
    }

    @GetMapping("/findAllUser")
    public String findAllUsers(@AuthenticationPrincipal User user){
        System.out.println("了解登录用户是谁:"+user); //使用AuthenticationPrincipal注解,User是用户详情服务中的用户对象
        if(user!=null){
            System.out.println("了解登录用户是谁:"+user.getUsername()+"||"+user.getPassword()+"||"+user.getEnabled());
        }
        List<AtblUserLv> list= userService.findAllUser();
        Iterator<AtblUserLv> iter = list.iterator();
        while (iter.hasNext()){
            AtblUserLv atblUserLv=iter.next();
            System.out.println(atblUserLv.getId()+"||"+atblUserLv.getUsername()+"||"+atblUserLv.getPassword());
        }
        return "success";
    }

    @GetMapping("/hiRole")
    @ResponseBody //这个注解此方法表示返回字符串内容hello security
    public String helloRole(){
        System.out.println("-----helloRole----");
        return "hello Role!";
    }

    @GetMapping("/hiAnyRole")
    @ResponseBody //这个注解此方法表示返回字符串内容hello security
    public String helloAnyRole(){
        System.out.println("-----helloAnyRole----");
        return "hello AnyRole!";
    }

    @GetMapping("/findMenu")
    public String findMenus(@AuthenticationPrincipal User user, HttpServletRequest request, HttpServletResponse response){
        List<Menus> listMenu=new ArrayList<>();
        System.out.println("findMenus开始执行:---------------------------");
        //User users=myUserRepository.findByUsername(user.getUsername());
        for(SysRole role : user.getRoles()){
            System.out.println("角色:"+role.getRole()+"======="+role.getPermissions());
            for(SysPermission permission : role.getPermissions()){
                if(permission.getResourceType().equals("menu")){
                    Menus menu=new Menus();
                    menu.setId(permission.getId());
                    menu.setName(permission.getName());
                    menu.setParentId(permission.getParentId());
                    menu.setParentIds(permission.getParentIds());
                    menu.setUrl(permission.getUrl());
                    listMenu.add(menu);
                }
            }
        }
        request.setAttribute("listMenus",listMenu);

        for(Menus menus:listMenu){
            if(menus.getParentId()==10000){ //10000为数据库中的值
                System.out.println("=="+menus.getName()+"["+menus.getUrl()+"]");
                for(Menus menusch:listMenu){
                    if(menus.getId()==menusch.getParentId()){
                        System.out.println("---------"+menusch.getName()+"["+menusch.getUrl()+"]");
                    }
                }
            }
        }
        return "menus";
    }
}

8. 定义保存菜单数据的bean类

package com.securitydemo.entity;

/**
 * 定义的实体类,保存菜单数据
 */
public class Menus {
    private long id;
    private String name;
    private Long parentId;
    private Long parentIds;
    private String url;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public Long getParentIds() {
        return parentIds;
    }

    public void setParentIds(Long parentIds) {
        this.parentIds = parentIds;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

9. 实现菜单显示页menus.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>菜单列表</title>
</head>
<body>
<h3>欢迎进入系统菜单</h3>
<div th:unless="${#lists.isEmpty(listMenus)}">
    <dl th:each="menu : ${listMenus}">
        <div th:if="${menu.parentId eq 10000}">
            <dt>
                <span>■</span>
                <span th:text="${menu.name}"></span>:
                <span th:text="${menu.url}"></span>
            </dt>
            <dl th:each="menusch : ${listMenus}">
                <div th:if="${menusch.parentId eq menu.id}">
                    <dt>
                        <span>&emsp;&emsp;●</span>
                        <span th:text="${menusch.name}"></span>:
                        <span th:text="${menusch.url}"></span>
                    </dt>
                </div>

            </dl>
        </div>
    </dl>
</div>
<div th:if="${#lists.isEmpty(listMenus)}">
    <p>没有查询到菜单!</p>
</div>
<br> <a href="/logout">退出</a>
</body>
</html>

10. 登录认证、URL访问权限验证通过后的页面效果

11. 工程目录结构


总结

        本文重点内容是在开发步骤4、5、6步的RBAC权限模型的定义类RbacServiceImpl的实现上和配置类SecurityConfig中access("@rbacService.hasPermission(request,authentication)")方法的使用。另附带一个菜单页面的实现。

        本文是接另一遍文章“spirng框架之spring security(一)”的扩展,所以想要比较系统的了解可以先看“spirng框架之spring security(一)”,连接:(76条消息) spirng框架之spring security(一)_lv12345678的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/u011529483/article/details/123572622

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值