基于session实现认证授权
1.基本概念
1.2 什么是认证
简而言之,系统中的登录即为认证,认证用户的身份是否合法。进行系统认证可以有效的实现保护用户的隐私数据以及资源,用户的身份合法即可访问该资源。
用户认证就是判断一个用户的身份是否合法的过程,系统去访问资源时,系统要求验证用户的身份信息,身份合法即可继续访问,不合法拒绝访问。常见的用户登录方式有:账号密码登录,二维码登录,手机短信登录,指纹认证,刷脸认证等等。
1.3 什么是会话
用户进行认证之后,为了避免用户每次的操作都需要进行认证,会话就是系统为了保持当前用户的登录状态所提供的机制,常见的方式有session,token等。
1.4 什么是授权
授权是通过用户权限来控制用户访问数据的过程,拥有资源的访问权限即可正常访问,没有则拒绝。(先认证后授权)
1.5 授权的数据模型
进行用户对访问资源的控制首先需要了解授权的数据模型
授权可以简单的理解为who对what(which)进行how操作
WHO: 即主体(subject),主体一般指的是用户,也可以是程序访问系统中的资源
WHAT:即资源(resource),如系统菜单,页面,按钮,代码方法,系统商品信息,系统订单信息等。系统菜单,页面,按钮,代码方法都属于系统功能资源, 对于web系统每个功能资源通常用一个url,系统商品信息,系统订单信息都属于实体资源(数据资源)。实体资源由资源类型以及资源实例组成,比如商品信息为资源类型,商品编号为001的为资源实例。
HOW:权限/许可(Permission),规定了用户对资源的操作许可,权限离开资源没有意义,如用户查询权限,用户添加权限等等,通过权限可知用户对哪些资源拥有操作的许可。
用户 角色 权限 以及关联表(共5张表)
1.6 RBAC
基于角色的访问(role --),需要改代码,显得冗余。
基于权限的访问控制(resource Based Access Controller),只要给角色分配权限,判断用户是否有该权限即可实现该权限。
2.基于session实现认证
更深刻的认识认证会话授权 (基于springBoot 以及 mybaitsPlus实现)
2.1用户登录认证
//controller
@GetMapping("/toLogin")
@ResponseBody
public ReturnT<String> login(@RequestParam("username")String username, @RequestParam("pwd")String pwd, HttpSession session){
AuthenticationRequest authenticationRequest = new AuthenticationRequest(username,pwd);
UserEntity userInfo = authenticationService.getUserInfo(authenticationRequest);
session.setAttribute(Constant.USER_SESSION_ID, userInfo.getUsername());
return ReturnT.success(userInfo.getUsername()+"登录成功");
}
//Service
@Autowired
private UserMapper userMapper;
@Override
public UserEntity getUserInfo(AuthenticationRequest authenticationRequest) {
//先判断认证参数是否为空
if(authenticationRequest == null
|| StringUtils.isEmpty(authenticationRequest.getUsername())
|| StringUtils.isEmpty(authenticationRequest.getPwd())){
throw new RuntimeException("用户的账号密码信息为空");
}
//查询用户相关的信息
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username",authenticationRequest.getUsername());
UserEntity userEntity = userMapper.selectOne(queryWrapper);
//判断是否存在该用户
if(userEntity == null){
throw new RuntimeException("查询不到该用户");
}
//判断密码是否正确
if(!authenticationRequest.getPwd().equals(userEntity.getPwd())){
throw new RuntimeException("用户名或者密码不正确");
}
//认证通过,返回用户信息
return userEntity;
}
2.2 实现用户会话功能
用户登录成功之后将用户信息添加到session中,当用户访问页面的时候携带session才能实现对资源的访问,退出登录访问资源失败。
后台相关实现代码如下所示
/**
* @author daiguojun
* @date 2021-07-13 22:29
* 登录控制层
*/
@Controller
@RequestMapping("system")
@Slf4j
public class LoginController {
@Autowired
private AuthenticationService authenticationService;
@GetMapping("/toLogin")
@ResponseBody
public ReturnT<String> login(@RequestParam("username")String username, @RequestParam("pwd")String pwd, HttpSession session){
AuthenticationRequest authenticationRequest = new AuthenticationRequest(username,pwd);
UserEntity userInfo = authenticationService.getUserInfo(authenticationRequest);
session.setAttribute(Constant.USER_SESSION_ID, userInfo.getUsername());
return ReturnT.success(userInfo.getUsername()+"登录成功");
}
@GetMapping("login")
public String toLogin(){
return "login";
}
@GetMapping("/loginOut")
@ResponseBody
public ReturnT<String> loginOut(HttpSession session){
session.removeAttribute(Constant.USER_SESSION_ID);
return ReturnT.success("退出登录成功");
}
@GetMapping("/testSession")
@ResponseBody
public ReturnT<String> testSession(HttpSession session){
String fullName = null;
Object attribute = session.getAttribute(Constant.USER_SESSION_ID);
if(attribute == null){
fullName = "匿名";
return ReturnT.failed(fullName);
}else{
return ReturnT.success("可以访问资源"+attribute);
}
}
}
2.3 用户对资源的访问实现
- 创建角色表,资源表,以及中间关联表,根据用户登录信息获取到用户的相对应权限
List<ResourceEntity> resourceEntityList = userMapper.getResourceList(userEntity.getId());
UserForm userForm = new UserForm();
BeanUtils.copyProperties(userEntity,userForm);
userForm.setResourceEntityList(resourceEntityList);
- 添加拦截器
/**
* @author daiguojun
* @date 2021-07-13 22:29
* 登录拦截器
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 调用方法前执行,根据用户身份对访问资源进行校验
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从session中取出用户的信息
Object attribute = request.getSession().getAttribute(Constant.USER_SESSION_ID);
if(attribute == null){
//相应未登陆信息给前端
getResponseMsg(response,"请登陆");
}
//session中有信息说明用户以登陆,获取用户相对应的资源
UserForm userForm = (UserForm)attribute;
// 将用户访问的url 与 用户权限比较,如果url中包含用户权限,则可以访问
String url = request.getRequestURI();
List<ResourceEntity> resourceList = userForm.getResourceEntityList();
for (ResourceEntity resourceEntity:resourceList){
if(url.contains(resourceEntity.getResource())){
return true;
}
}
getResponseMsg(response,"没有权限,拒绝访问");
return false;
}
private void getResponseMsg(HttpServletResponse response, String message) throws IOException {
PrintWriter writer = response.getWriter();
writer.print(message);
writer.close();
response.resetBuffer();
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
/**
* @author daiguojun
* @date 2021-07-13 22:29
* 配置文件使得拦截器生效
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置只拦截某一个controller
registry.addInterceptor(loginInterceptor).addPathPatterns("/user/**");
}
}
以上就实现了一个简单的基于session的登录认证授权功能。不同的用户登录之后只能访问到自己相对应权限的接口
文中只有部分关键代码,完整代码以提交到gitee:https://gitee.com/dai_guojun/springboot-security.git