zoukankan      html  css  js  c++  java
  • Springboot +Shiro 前后端分离式权限管理系统

     前后端分离
    要实现前后端分离,需要考虑以下2个问题: 1. 项目不再基于session了,如何知道访问者是谁? 2. 如何确认访问者的权限?


    前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。

    代码已上传到Git:https://github.com/FENGZHIJIE1998/shiro-demo

    Let's do it!!


    介绍:这次我们使用SpringBoot+SpringJPA+Swagger+Shiro快速搭建前后端分离的权限管理系统 利用JPA帮我们管理数据库,Swagger帮我搭建Web测试环境;注意:主要观察token的使用方法!

    第一步:新建工程,pom文件application.yml巴拉巴拉这里省略,这里贴出需要用到的依赖

     
    <dependencies>
     
    <dependency>
     
    <groupId>org.springframework.boot</groupId>
     
    <artifactId>spring-boot-starter</artifactId>
     
    </dependency>
     
     
     
    <dependency>
     
    <groupId>org.springframework.boot</groupId>
     
    <artifactId>spring-boot-starter-test</artifactId>
     
    <scope>test</scope>
     
    </dependency>
     
     
     
    <!--web-->
     
    <dependency>
     
    <groupId>org.springframework.boot</groupId>
     
    <artifactId>spring-boot-starter-web</artifactId>
     
    </dependency>
     
     
     
    <!--JPA-->
     
    <dependency>
     
    <groupId>org.springframework.boot</groupId>
     
    <artifactId>spring-boot-starter-data-jpa</artifactId>
     
    </dependency>
     
     
     
    <!--JDBC-->
     
    <dependency>
     
    <groupId>org.springframework.boot</groupId>
     
    <artifactId>spring-boot-starter-jdbc</artifactId>
     
    </dependency>
     
     
     
    <!--lombok-->
     
    <dependency>
     
    <groupId>org.projectlombok</groupId>
     
    <artifactId>lombok</artifactId>
     
    </dependency>
     
     
     
    <!-- shiro-->
     
    <dependency>
     
    <groupId>org.apache.shiro</groupId>
     
    <artifactId>shiro-spring</artifactId>
     
    <version>1.3.2</version>
     
    </dependency>
     
    <!--mysql-connector-->
     
    <dependency>
     
    <groupId>mysql</groupId>
     
    <artifactId>mysql-connector-java</artifactId>
     
    <scope>runtime</scope>
     
    </dependency>
     
    <!--druid-->
     
    <dependency>
     
    <groupId>com.alibaba</groupId>
     
    <artifactId>druid</artifactId>
     
    <version>1.0.31</version>
     
    </dependency>
     
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
     
    <dependency>
     
    <groupId>com.alibaba</groupId>
     
    <artifactId>druid-spring-boot-starter</artifactId>
     
    <version>1.1.10</version>
     
    </dependency>
     
    <!-- swagger -->
     
    <dependency>
     
    <groupId>com.spring4all</groupId>
     
    <artifactId>swagger-spring-boot-starter</artifactId>
     
    <version>1.8.0.RELEASE</version>
     
    </dependency>
     
    <!-- swagger生成接口API -->
     
    <dependency>
     
    <groupId>io.springfox</groupId>
     
    <artifactId>springfox-swagger2</artifactId>
     
    <version>2.7.0</version>
     
    </dependency>
     
    <!-- 接口API生成html文档 -->
     
    <dependency>
     
    <groupId>io.springfox</groupId>
     
    <artifactId>springfox-swagger-ui</artifactId>
     
    <version>2.7.0</version>
     
    </dependency>
     
    <!--json工具-->
     
    <dependency>
     
    <groupId>com.alibaba</groupId>
     
    <artifactId>fastjson</artifactId>
     
    <version>1.2.13</version>
     
    </dependency>
     
    <!--gson -->
     
    <dependency>
     
    <groupId>com.google.code.gson</groupId>
     
    <artifactId>gson</artifactId>
     
    <version>2.8.5</version>
     
    </dependency>
     
    <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
     
    <dependency>
     
    <groupId>commons-lang</groupId>
     
    <artifactId>commons-lang</artifactId>
     
    <version>2.6</version>
     
    </dependency>
     
     
     
    </dependencies>

    第二步:准备好要用的包包和类类

    第三步:编写登陆入口(为了方便这里不做密码加盐加密):

    /**
     
    * @Author CrazyJay
     
    * @Date 2019/3/30 22:04
     
    * @Version 1.0
     
    */
     
    @RestController
     
    public class ShiroController {
     
     
     
    @Autowired
     
    private ShiroService shiroService;
     
     
     
    /**
     
    * 登录
     
    */
     
    @ApiOperation(value = "登陆", notes = "参数:用户名 密码")
     
    @GetMapping("/sys/login")
     
    public Map<String, Object> login(String username, String password) {
     
    Map<String, Object> result = new HashMap<>();
     
    //用户信息
     
    User user = shiroService.findByUsername(username);
     
    //账号不存在、密码错误
     
    if (user==null||!user.getPassword().equals(password)) {
     
    result.put("status", "400");
     
    result.put("msg", "账号或密码有误");
     
    return result;
     
    } else {
     
    //生成token,并保存到数据库
     
    result = shiroService.createToken(user.getUserId());
     
    result.put("status", "200");
     
    result.put("msg", "登陆成功");
     
    return result;
     
    }
     
    }
     
     
     
    /**
     
    * 退出
     
    */
     
    @PostMapping("/sys/logout")
     
    public Map<String, Object> logout() {
     
    Map<String, Object> result = new HashMap<>();
     
    User user = (User) SecurityUtils.getSubject().getPrincipal();
     
    shiroService.logout(user.getUserId());
     
    result.put("status", "200");
     
    result.put("msg", "登陆成功");
     
    return result;
     
    }
     
    }

    第四步:编写ShiroService中的方法

     
    /**
     
    * @Author CrazyJay
     
    * @Date 2019/3/30 22:18
     
    * @Version 1.0
     
    */
     
    @Service
     
    public class ShiroServiceImpl implements ShiroService {
     
     
     
     
     
    @Autowired
     
    private UserRepository userRepository;
     
    @Autowired
     
    private RoleRepository roleRepository;
     
    @Autowired
     
    private PermissionRepository permissionRepository;
     
    @Autowired
     
    private SysTokenRepository sysTokenRepository;
     
     
     
    /**
     
    * 根据username查找用户
     
    *
     
    * @param username
     
    * @return User
     
    */
     
    @Override
     
    public User findByUsername(String username) {
     
    User user = userRepository.findByUsername(username);
     
    return user;
     
    }
     
     
     
    //12小时后过期
     
    private final static int EXPIRE = 3600 * 12;
     
     
     
    @Override
     
    /** 重点!!
     
    * 生成一个token
     
    *@param [userId]
     
    *@return Result
     
    */
     
    public Map<String,Object> createToken(Integer userId) {
     
    Map<String,Object> result = new HashMap<>();
     
    //生成一个token
     
    String token = TokenGenerator.generateValue();
     
    //当前时间
     
    Date now = new Date();
     
    //过期时间
     
    Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
     
    //判断是否生成过token
     
    SysToken tokenEntity = sysTokenRepository.findByUserId(userId);
     
    if (tokenEntity == null) {
     
    tokenEntity = new SysToken();
     
    tokenEntity.setUserId(userId);
     
    tokenEntity.setToken(token);
     
    tokenEntity.setUpdateTime(now);
     
    tokenEntity.setExpireTime(expireTime);
     
    //保存token
     
    sysTokenRepository.save(tokenEntity);
     
    } else {
     
    tokenEntity.setToken(token);
     
    tokenEntity.setUpdateTime(now);
     
    tokenEntity.setExpireTime(expireTime);
     
    //更新token
     
    sysTokenRepository.save(tokenEntity);
     
    }
     
    //返回token给前端
     
    result.put("token", token);
     
    result.put("expire", EXPIRE);
     
    return result;
     
    }
     
     
     
    @Override
     
    public void logout(Integer userId) {
     
    //生成一个token
     
    String token = TokenGenerator.generateValue();
     
    //修改token
     
    SysToken tokenEntity = new SysToken();
     
    tokenEntity.setUserId(userId);
     
    tokenEntity.setToken(token);
     
    sysTokenRepository.save(tokenEntity);
     
    }
     
     
     
    @Override
     
    public SysToken findByToken(String accessToken) {
     
    return sysTokenRepository.findByToken(accessToken);
     
     
     
    }
     
     
     
    @Override
     
    public User findByUserId(Integer userId) {
     
    return userRepository.findByUserId(userId);
     
    }
     
    }

    第四步:编写ShiroConfig类

     
    /**
     
    * @Author CrazyJay
     
    * @Date 2019/3/30 21:50
     
    * @Version 1.0
     
    */
     
    @Configuration
     
    public class ShiroConfig {
     
     
     
    @Bean("securityManager")
     
    public SecurityManager securityManager(AuthRealm authRealm) {
     
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
     
    //设置自定义的Realm
     
    securityManager.setRealm(authRealm);
     
    securityManager.setRememberMeManager(null);
     
    return securityManager;
     
    }
     
     
     
    @Bean("shiroFilter")
     
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
     
    ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
     
    shiroFilter.setSecurityManager(securityManager);
     
    //自定义过滤(关键)
     
    Map<String, Filter> filters = new HashMap<>();
     
    filters.put("auth", new AuthFilter());
     
    shiroFilter.setFilters(filters);
     
    Map<String, String> filterMap = new LinkedHashMap<>();
     
    filterMap.put("/webjars/**", "anon");
     
    filterMap.put("/druid/**", "anon");
     
    filterMap.put("/sys/login", "anon");
     
    filterMap.put("/swagger/**", "anon");
     
    filterMap.put("/v2/api-docs", "anon");
     
    filterMap.put("/swagger-ui.html", "anon");
     
    filterMap.put("/swagger-resources/**", "anon");
     
    filterMap.put("/**", "auth");
     
    shiroFilter.setFilterChainDefinitionMap(filterMap);
     
    //这里不需要设置什么登陆路径之类的
     
    return shiroFilter;
     
    }
     
     
     
    @Bean("lifecycleBeanPostProcessor")
     
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
     
    return new LifecycleBeanPostProcessor();
     
    }
     
     
     
    @Bean
     
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
     
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
     
    advisor.setSecurityManager(securityManager);
     
    return advisor;
     
    }
     
    }

    第五步:实现自定义AuthenticatingFilter。调用接口时,接受传过来的token后,如何保证token有效及用户权限呢?其实,Shiro提 供了AuthenticatingFilter抽象类,继承AuthenticatingFilter抽象类即可。这个过滤器是实现前后端分离的重中之重!

    /**
     
    * auth过滤器
     
    *
     
    * @Author CrazyJay
     
    * @Date 2019/3/31 10:38
     
    * @Version 1.0
     
    */
     
    public class AuthFilter extends AuthenticatingFilter {
     
     
     
    /**
     
    * 生成自定义token
     
    * @param request
     
    * @param response
     
    * @return
     
    * @throws Exception
     
    */
     
    @Override
     
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
     
    //获取请求token
     
    String token = getRequestToken((HttpServletRequest) request);
     
    if (StringUtils.isBlank(token)) {
     
    return null;
     
    }
     
    return new AuthToken(token);
     
    }
     
     
     
    /**
     
    * 步骤1.所有请求全部拒绝访问
     
    * @param request
     
    * @param response
     
    * @param mappedValue
     
    * @return
     
    */
     
    @Override
     
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
     
    if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
     
    return true;
     
    }
     
    return false;
     
    }
     
     
     
    /**
     
    * 步骤2,拒绝访问的请求,会调用onAccessDenied方法,onAccessDenied方法先获取 token,再调用executeLogin方法
     
    * @param request
     
    * @param response
     
    * @return
     
    * @throws Exception
     
    */
     
    @Override
     
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
     
    //获取请求token,如果token不存在,直接返回
     
    String token = getRequestToken((HttpServletRequest) request);
     
    if (StringUtils.isBlank(token)) {
     
    HttpServletResponse httpResponse = (HttpServletResponse) response;
     
    httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
     
    httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
     
    httpResponse.setCharacterEncoding("UTF-8");
     
    Map<String, Object> result = new HashMap<>();
     
    result.put("status", "400");
     
    result.put("msg", "未登录--onAccessDenied");
     
    String json = new Gson().toJson(result);
     
    httpResponse.getWriter().print(json);
     
    return false;
     
    }
     
    return executeLogin(request, response);
     
    }
     
    /**
     
    * 登陆失败时候调用
     
    */
     
    @Override
     
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
     
    HttpServletResponse httpResponse = (HttpServletResponse) response;
     
    httpResponse.setContentType("application/json;charset=utf-8");
     
    httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
     
    httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
     
    httpResponse.setCharacterEncoding("UTF-8");
     
    try {
     
    //处理登录失败的异常
     
    Throwable throwable = e.getCause() == null ? e : e.getCause();
     
    Map<String, Object> result = new HashMap<>();
     
    result.put("status", "400");
     
    result.put("msg", "登陆失败--onLoginFailure");
     
    String json = JSON.toJSONString(result);
     
    httpResponse.getWriter().print(json);
     
    } catch (IOException e1) {
     
    }
     
    return false;
     
    }
     
    /**
     
    * 获取请求的token
     
    */
     
    private String getRequestToken(HttpServletRequest httpRequest) {
     
     
     
    //从header中获取token
     
    String token = httpRequest.getHeader("token");
     
    //如果header中不存在token,则从参数中获取token
     
    if (StringUtils.isBlank(token)) {
     
    token = httpRequest.getParameter("token");
     
    }
     
    return token;
     
    }
     
    }

    第六步:实现自定义的AuthenticationToken。阅读AuthenticatingFilter抽象类中executeLogin方法,我们发现调用 了subject.login(token),这是shiro的登录方法,且需要token参数,我们自定义 AuthToken类,只要实现AuthenticationToken接口,就可以了。

     
    //AuthenticatingFilter中的executeLogin()
     
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
     
    AuthenticationToken token = createToken(request, response);
     
    if (token == null) {
     
    String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
     
    "must be created in order to execute a login attempt.";
     
    throw new IllegalStateException(msg);
     
    }
     
    try {
     
    Subject subject = getSubject(request, response);
     
    //重点!
     
    subject.login(token);
     
    return onLoginSuccess(token, subject, request, response);
     
    } catch (AuthenticationException e) {
     
    return onLoginFailure(token, e, request, response);
     
    }
     
    }
     
    /**
     
    * 自定义AuthenticationToken类
     
    * @Author CrazyJay
     
    * @Date 2019/3/31 10:58
     
    * @Version 1.0
     
    */
     
    public class AuthToken implements AuthenticationToken {
     
     
     
    private String token;
     
     
     
    public AuthToken(String token) {
     
    this.token = token;
     
    }
     
     
     
    @Override
     
    public Object getPrincipal() {
     
    return token;
     
    }
     
     
     
    @Override
     
    public Object getCredentials() {
     
    return token;
     
    }
     
    }

    这里我实现的时候出现了Token不匹配的Bug。DeBug下可以查到源头是代码是用UsernamePasswordToken.class和我自定义的AuthToken.class配对。按道理应该是true,却返回了false...于是我就把自定义的AuthToken不实现AuthenticationToken,转为继承UsernamePasswordToken,就可以了。(renren-fast中却可以,可能是版本的问题)

    第七步:编写自己的Realm

    /**
     
    * @Author CrazyJay
     
    * @Date 2019/3/30 21:38
     
    * @Version 1.0
     
    */
     
    @Component
     
    public class AuthRealm extends AuthorizingRealm {
     
     
     
    @Autowired
     
    private ShiroService shiroService;
     
     
     
    /**
     
    * 授权(验证权限时候调用
     
    *@param [principals]
     
    *@return org.apache.shiro.authz.AuthorizationInfo
     
    */
     
    @Override
     
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
    //1. 从 PrincipalCollection 中来获取登录用户的信息
     
    User user = (User) principals.getPrimaryPrincipal();
     
    //Integer userId = user.getUserId();
     
    //2.添加角色和权限
     
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
     
    for (Role role : user.getRoles()) {
     
    //2.1添加角色
     
    simpleAuthorizationInfo.addRole(role.getRoleName());
     
    for (Permission permission : role.getPermissions()) {
     
    //2.1.1添加权限
     
    simpleAuthorizationInfo.addStringPermission(permission.getPermission());
     
    }
     
    }
     
    return simpleAuthorizationInfo;
     
    }
     
     
     
    @Override
     
    /**
     
    * 认证(登陆时候调用)
     
    *@param [token]
     
    *@return org.apache.shiro.authc.AuthenticationInfo
     
    */
     
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     
    String accessToken = (String) token.getPrincipal();
     
    //1. 根据accessToken,查询用户信息
     
    SysToken tokenEntity = shiroService.findByToken(accessToken);
     
    //2. token失效
     
    if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
     
    throw new IncorrectCredentialsException("token失效,请重新登录");
     
    }
     
    //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
     
    User user = shiroService.findByUserId(tokenEntity.getUserId());
     
    //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
     
    if (user == null) {
     
    throw new UnknownAccountException("用户不存在!");
     
    }
     
    //5. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
     
    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, this.getName());
     
    return info;
     
    }
     
    }

    第八步:,登录失败后,则调用AuthFilter中的onLoginFailure(),进行失败处理,整个流程结束。

    /**
     
    * 登陆失败时候调用
     
    */
     
    @Override
     
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
     
    HttpServletResponse httpResponse = (HttpServletResponse) response;
     
    httpResponse.setContentType("application/json;charset=utf-8");
     
    httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
     
    httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
     
    httpResponse.setCharacterEncoding("UTF-8");
     
    try {
     
    //处理登录失败的异常
     
    Throwable throwable = e.getCause() == null ? e : e.getCause();
     
    Map<String, Object> result = new HashMap<>();
     
    result.put("status", "400");
     
    result.put("msg", "登陆失败--onLoginFailure");
     
    String json = JSON.toJSONString(result);
     
    httpResponse.getWriter().print(json);
     
    } catch (IOException e1) {
     
    }
     
    return false;
     
    }

    第九步:登录成功后,则调用doGetAuthorizationInfo方法,查询用户的权限,再调用具体的接口,整个流程结束。

    /**
     
    * 授权(验证权限时候调用
     
    *@param [principals]
     
    *@return org.apache.shiro.authz.AuthorizationInfo
     
    */
     
    @Override
     
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     
    //1. 从 PrincipalCollection 中来获取登录用户的信息
     
    User user = (User) principals.getPrimaryPrincipal();
     
    //Integer userId = user.getUserId();
     
    //2.添加角色和权限
     
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
     
    for (Role role : user.getRoles()) {
     
    //2.1添加角色
     
    simpleAuthorizationInfo.addRole(role.getRoleName());
     
    for (Permission permission : role.getPermissions()) {
     
    //2.1.1添加权限
     
    simpleAuthorizationInfo.addStringPermission(permission.getPermission());
     
    }
     
    }
     
    return simpleAuthorizationInfo;
     
    }

    然后我们来看看效果:

    实体类

    @Getter
     
    @Setter
     
    @Entity
     
    public class User {
     
    @Id
     
    private Integer userId;
     
     
     
    private String username;
     
    private String password;
     
     
     
    @ManyToMany(fetch = FetchType.EAGER)
     
    @JoinTable(name = "user_role",
     
    joinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "userId")},
     
    inverseJoinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")})
     
    private Set<Role> roles;
     
     
     
    }
     
     
     
    @Getter
     
    @Setter
     
    @Entity
     
    public class Role {
     
     
     
    @Id
     
    private Integer roleId;
     
    private String roleName;
     
     
     
    @ManyToMany(fetch = FetchType.EAGER)
     
    @JoinTable(name = "role_permission",
     
    joinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")},
     
    inverseJoinColumns = {@JoinColumn(name = "PERMISSION_ID", referencedColumnName = "permissionId")})
     
    private Set<Permission> permissions;
     
    }
     
     
     
    @Getter
     
    @Setter
     
    @Entity
     
    public class Permission {
     
     
     
    @Id
     
    private Integer permissionId;
     
    private String permissionName;
     
    private String permission;
     
    }
     
     
     
    @Getter
     
    @Setter
     
    @Entity
     
    public class SysToken{
     
     
     
    @Id
     
    private Integer userId;
     
    private String token;
     
    private Date expireTime;
     
    private Date updateTime
     
    }

    以及给实体类附上权限:

    我定义了三个用户 

    用户 角色 权限
    Jack SVIP select;save;delete;update
    Rose VIP select;save;update
    Paul P select
    /*
     
    Navicat MySQL Data Transfer
     
    Source Server : localhost
     
    Source Server Version : 50549
     
    Source Host : localhost:3306
     
    Source Database : shiro
     
    Target Server Type : MYSQL
     
    Target Server Version : 50549
     
    File Encoding : 65001
     
    Date: 2019-04-07 17:06:36
     
    */
     
     
     
    SET FOREIGN_KEY_CHECKS=0;
     
     
     
    -- ----------------------------
     
    -- Table structure for permission
     
    -- ----------------------------
     
    DROP TABLE IF EXISTS `permission`;
     
    CREATE TABLE `permission` (
     
    `permission_id` int(11) NOT NULL,
     
    `permission` varchar(255) DEFAULT NULL,
     
    `permission_name` varchar(255) DEFAULT NULL,
     
    PRIMARY KEY (`permission_id`)
     
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
     
     
    -- ----------------------------
     
    -- Records of permission
     
    -- ----------------------------
     
    INSERT INTO `permission` VALUES ('1', 'select', '查看');
     
    INSERT INTO `permission` VALUES ('2', 'update', '更新');
     
    INSERT INTO `permission` VALUES ('3', 'delete', '删除');
     
    INSERT INTO `permission` VALUES ('4', 'save', '新增');
     
     
     
    -- ----------------------------
     
    -- Table structure for role
     
    -- ----------------------------
     
    DROP TABLE IF EXISTS `role`;
     
    CREATE TABLE `role` (
     
    `role_id` int(11) NOT NULL,
     
    `role_name` varchar(255) DEFAULT NULL,
     
    PRIMARY KEY (`role_id`)
     
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
     
     
    -- ----------------------------
     
    -- Records of role
     
    -- ----------------------------
     
    INSERT INTO `role` VALUES ('1', 'svip');
     
    INSERT INTO `role` VALUES ('2', 'vip');
     
    INSERT INTO `role` VALUES ('3', 'p');
     
     
     
    -- ----------------------------
     
    -- Table structure for role_permission
     
    -- ----------------------------
     
    DROP TABLE IF EXISTS `role_permission`;
     
    CREATE TABLE `role_permission` (
     
    `role_id` int(11) NOT NULL,
     
    `permission_id` int(11) NOT NULL,
     
    PRIMARY KEY (`role_id`,`permission_id`),
     
    KEY `FKf8yllw1ecvwqy3ehyxawqa1qp` (`permission_id`),
     
    CONSTRAINT `FKa6jx8n8xkesmjmv6jqug6bg68` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`),
     
    CONSTRAINT `FKf8yllw1ecvwqy3ehyxawqa1qp` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`permission_id`)
     
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
     
     
    -- ----------------------------
     
    -- Records of role_permission
     
    -- ----------------------------
     
    INSERT INTO `role_permission` VALUES ('1', '1');
     
    INSERT INTO `role_permission` VALUES ('2', '1');
     
    INSERT INTO `role_permission` VALUES ('3', '1');
     
    INSERT INTO `role_permission` VALUES ('1', '2');
     
    INSERT INTO `role_permission` VALUES ('2', '2');
     
    INSERT INTO `role_permission` VALUES ('1', '3');
     
    INSERT INTO `role_permission` VALUES ('1', '4');
     
    INSERT INTO `role_permission` VALUES ('2', '4');
     
     
     
    -- ----------------------------
     
    -- Table structure for user
     
    -- ----------------------------
     
    DROP TABLE IF EXISTS `user`;
     
    CREATE TABLE `user` (
     
    `user_id` int(11) NOT NULL,
     
    `password` varchar(255) DEFAULT NULL,
     
    `username` varchar(255) DEFAULT NULL,
     
    PRIMARY KEY (`user_id`)
     
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
     
     
    -- ----------------------------
     
    -- Records of user
     
    -- ----------------------------
     
    INSERT INTO `user` VALUES ('1', '123', 'Jack');
     
    INSERT INTO `user` VALUES ('2', '123', 'Rose');
     
    INSERT INTO `user` VALUES ('3', '123', 'Paul');
     
     
     
    -- ----------------------------
     
    -- Table structure for user_role
     
    -- ----------------------------
     
    DROP TABLE IF EXISTS `user_role`;
     
    CREATE TABLE `user_role` (
     
    `user_id` int(11) NOT NULL,
     
    `role_id` int(11) NOT NULL,
     
    PRIMARY KEY (`user_id`,`role_id`),
     
    KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`),
     
    CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`),
     
    CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`)
     
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     
     
     
    -- ----------------------------
     
    -- Records of user_role
     
    -- ----------------------------
     
    INSERT INTO `user_role` VALUES ('1', '1');
     
    INSERT INTO `user_role` VALUES ('2', '2');
     
    INSERT INTO `user_role` VALUES ('3', '3');

    测试类:因为我是用Swagger来测试,所以为了方便就直接传递token参数。具体开发时候可由前端把接收到的token放入Header。

    /**
     
    * @Author CrazyJay
     
    * @Date 2019/4/7 15:20
     
    * @Version 1.0
     
    */
     
    @RestController("/test")
     
    public class TestController {
     
     
     
     
     
    @RequiresPermissions({"save"}) //没有的话 AuthorizationException
     
    @PostMapping("/save")
     
    public Map<String, Object> selectRole(String token) {
     
     
     
    System.out.println("save");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有save的权力");
     
    return map;
     
    }
     
     
     
    @RequiresPermissions({"delete"}) //没有的话 AuthorizationException
     
    @PostMapping("/delete")
     
    public Map<String, Object> managerRole(String token) {
     
    System.out.println("delete");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有delete的权力");
     
    return map;
     
    }
     
     
     
    @RequiresPermissions({"update"}) //没有的话 AuthorizationException
     
    @PostMapping("update")
     
    public Map<String, Object> selectUser(String token) {
     
    System.out.println("update");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有update的权力");
     
    return map;
     
    }
     
     
     
    @RequiresPermissions({"select"}) //没有的话 AuthorizationException
     
    @PostMapping("select")
     
    public Map<String, Object> managerUser(String token) {
     
    System.out.println("select");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有select的权力");
     
    return map;
     
    }
     
     
     
    @RequiresRoles({"vip"}) //没有的话 AuthorizationException
     
    @PostMapping("/vip")
     
    public Map<String, Object> vip(String token) {
     
    System.out.println("vip");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有VIP角色");
     
    return map;
     
    }
     
    @RequiresRoles({"svip"}) //没有的话 AuthorizationException
     
    @PostMapping("/svip")
     
    public Map<String, Object> SVIP(String token) {
     
    System.out.println("svip");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有SVIP角色");
     
    return map;
     
    }
     
    @RequiresRoles({"p"}) //没有的话 AuthorizationException
     
    @PostMapping("/p")
     
    public Map<String, Object> P(String token) {
     
    System.out.println("p");
     
    Map<String, Object> map = new HashMap<String, Object>();
     
    map.put("success", true);
     
    map.put("msg", "当前用户有P角色");
     
    return map;
     
    }
     
    }

    启动项目来看看效果:记得带上token

    登陆成功:

    登陆失败:

    有某个角色时候:

    没有某个角色的时候:

    有某个权力时候:

    没有某个权力的时候:

    至此就已经进入尾声了

    总结:

    至于最后没有权利或角色返回的json字符串可以因为他抛出AuthorizationException。可以自定义全局异常处理器进行处理。通过这种token达到即可达到前后端分离开发。

  • 相关阅读:
    使用scrapy-redis 搭建分布式爬虫环境
    爬虫必备工具-chrome 开发者工具
    Python 中多进程、多线程、协程
    Python 中命令行参数解析工具 docopt 安装和应用
    什么是中台?
    ubuntu 18.04 上安装 docker
    深入理解 ajax系列第一篇(XHR 对象)
    scrapy 中 shell 出现 403 Forbiidden 解决方案
    python 的参数总结
    Python 的直接赋值、Deepcopy、Copy的区别
  • 原文地址:https://www.cnblogs.com/zhangrongfei/p/13815888.html
Copyright © 2011-2022 走看看