登录会话模型实战

登录会话模型实战

  • 简介
  • 操作流程
    • 数据库设计
    • curd操作工具
    • 登录鉴权实现
      • 登录代码
      • 用户资源查询
      • 登录接口
    • 认证和鉴权
      • ApplicationUtil工具类
      • GrantedAuthority权限控制类
      • security Filter鉴权
  • 测试
  • 总结
  • 源码

简介

登录会话是基本操作,不管哪个应用都会用到的模块,针对于此,个人简笔编写了一个简单案例,针对前面spring security的功能文章进行完善

操作流程

数据库设计

操作链接:用户中心-数据库设计

curd操作工具

操作链接:springboot植入pagerHelper和spring mybatis更新几种操作

登录鉴权实现

登录代码

这里制作简单的用户名和密码操作,查询用户资源路径,通过用户电话号码查询用户信息,匹配密码,具体代码如下:

 /**
     * 电话号码登录
     * @param phone 用户电话号码
     * @param password 加密后密码
     * @return
     */
    @Override
    public UserData login(String phone, @NotNull String password) {
     
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("userPhone", phone);
        User user = userMapper.selectOneByExample(example);
        if (user != null && password.equals(user.getUserPassword())) {
     
            UserData userData = new UserData();
            /**
             * 查询用户资源信息
             */
            userData.setUserResources(resourceMapper.selectResourceByRoleId(user.getId()));
            BeanUtils.copyProperties(user, userData);
            Logon logon = new Logon();
            logon.setToken(UUID.randomUUID().toString().replaceAll("-", ""));
            logon.setUserId(user.getId());
            try {
     
                logon.setResourceData(objectMapper.writeValueAsString(userData.getUserResources()));
            } catch (JsonProcessingException e) {
     
                e.printStackTrace();
            }
            Example logonExample = new Example(Logon.class);
            logonExample.createCriteria().andEqualTo("userId", user.getId());
            logonMapper.deleteByExample(logonExample);
            logonMapper.insert(logon);
            userData.setToken(logon.getToken());
            return userData;
        }
        return null;
    }

用户资源查询

查询用户资源的sql配置

 <select id="selectResourceByRoleId" resultMap="BaseResultMap">
        SELECT `tb_resource`.`id`,
          `resource_code`,
          `resource_url`,
          `resource_description`,
          `resource_state`
         FROM  `tb_resource`
         RIGHT  JOIN  `role_resource`
          ON `tb_resource`.`id` = `role_resource`.`resource_id`
         RIGHT  JOIN `user_role`
           ON `user_role`.`role_id` = `role_resource`.`role_id`
         WHERE `user_role`.`user_id` = #{userId}
    select>

登录接口

具体登录的controller代码

package com.lgh.controller;

import com.lgh.common.result.CommonResult;
import com.lgh.common.result.inter.IResult;
import com.lgh.model.domain.UserData;
import com.lgh.service.ILogonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

@Api(tags = "登录服务")
@RestController
@RequestMapping("/login")
@Validated
public class LogonController {
     

    @Autowired
    private ILogonService logonService;

    @ApiOperation("登录服务接口")
    @PostMapping("/sign")
    @Valid
    public IResult<UserData> login(@NotNull @RequestParam("phone") String phone, @NotNull @RequestParam("password") String password) {
     
        UserData userData = logonService.login(phone, password);
        return CommonResult.successData(userData);
    }
}

认证和鉴权

通过spring security的过滤器进行拦截认证,通过注解@RolesAllowed授权。由于spring security的filter不能给spring管理,否则会全局拦截,因此这里要获取请求接口,的拿到service的bean,所以先获取application的类。

ApplicationUtil工具类

package com.lgh.common.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationUtil implements ApplicationContextAware {
     
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     
        this.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> t) {
     
        return applicationContext.getBean(t);
    }
}

GrantedAuthority权限控制类

由于jsr250会添加前缀,个人不太喜欢前缀的ROLE_,因此我实现GrantedAuthority时也默认给前缀,如下代码

package com.lgh.common.authority.authentication;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

public class MySimpleGrantedAuthority implements GrantedAuthority {
     
    private static final long serialVersionUID = 510L;
    private final String rolePrefix = "ROLE_";
    private final String role;

    public MySimpleGrantedAuthority(String role) {
     
        Assert.hasText(role, "A granted authority textual representation is required");
        this.role = role;
    }

    public String getAuthority() {
     
        return rolePrefix + this.role;
    }

    public boolean equals(Object obj) {
     
        if (this == obj) {
     
            return true;
        } else {
     
            return obj instanceof MySimpleGrantedAuthority ? this.role.equals(((MySimpleGrantedAuthority) obj).role) : false;
        }
    }

    public int hashCode() {
     
        return this.role.hashCode();
    }

    public String toString() {
     
        return this.role;
    }
}

security Filter鉴权

下面我们来编写实际的过滤器

package com.lgh.common.authority.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lgh.common.authority.authentication.MyAuthentication;
import com.lgh.common.authority.authentication.MySimpleGrantedAuthority;
import com.lgh.common.authority.entity.UserDetail;
import com.lgh.common.result.CommonResult;
import com.lgh.common.util.ApplicationUtil;
import com.lgh.model.Resource;
import com.lgh.model.domain.UserData;
import com.lgh.service.ILogonService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * spring security过滤器,不要交给spring 管理
 */
public class MyAuthenticationFilter extends OncePerRequestFilter {
     


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
     
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
     
            filterChain.doFilter(request, response);
        } else {
     
            ILogonService logonService = ApplicationUtil.getBean(ILogonService.class);
            UserData userData = logonService.verify(token);
            if (userData == null) {
     
                ObjectMapper objectMapper = ApplicationUtil.getBean(ObjectMapper.class);
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().print(objectMapper.writeValueAsString(CommonResult.deny()));
                return;
            } else {
     
                UserDetail userDetail = new UserDetail();
                userDetail.setId(userData.getId());
                userDetail.setName(userData.getUserName());
                List<MySimpleGrantedAuthority> roles = new ArrayList<>();
                if (userData.getUserResources() != null) {
     
                    roles = userData.getUserResources().stream()
                            .map(Resource::getResourceCode)
                            .map(MySimpleGrantedAuthority::new)
                            .collect(Collectors.toList());
                }
                MyAuthentication myAuthentication = new MyAuthentication(userDetail, roles);
                SecurityContextHolder.getContext().setAuthentication(myAuthentication);
                filterChain.doFilter(request, response);
            }
        }
    }
}

测试

  1. 初始化用户信息,自己根据表格初始化
  2. swagger测试,如下图
    登录会话模型实战_第1张图片
  3. 登录完后,拿到对应的token,请求样例,如图
    登录会话模型实战_第2张图片

总结

  1. 从文章中可以学到简单的用户中心用户设计
  2. 如何使用pagerHelper功能
  3. 登录流程设计
  4. security鉴权使用和设计以及常见问题(过滤器不能交给spring,设置路径不校验web.ignoring(),权限校验前缀的处理

源码

github

你可能感兴趣的:(服务器,后端,java,java,spring,spring,boot)