认证基于Session,搞起来!!

什么是会话

用户通过认证后,为了避免用户的每次操作都进行认证可将用户的信息保存在会话中,会话就是系统为了保持当前用户的登录状态所提供的的机制,常见的有基于session方式、基于token方式等。

基于session的认证方式如下图:

它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session中,发给客户端的session_id存放到cookie中,这样用户客户端请求时带上session_id就可以验证服务器是否存在session数据,当用户退出系统或session过期销毁时,客户端的session_id也就失效了.

在这里插入图片描述

基于token方式如下图:

它交互流程流程是,用户认证成功后,服务端生成一个token发送给客户端,客户端可以放到cookie或者localStorage等存储中,每次请求时带上token,服务端收到token通过验证后即可确认用户身份.

图片二

授权的数据模型

授权可简单理解为Who对What进行How操作,包括如下:

Who:即主题,主题一般为用户,也可以是程序,需要访问系统中的资源.

What:即资源,如系统菜单、页面、按钮、代码方法、系统商品信息、系统订单信息。系统菜单、页面、按钮、代码方法都是属于系统功能资源。

How:权限许可,规定了用户对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个代码方法的调用权限、编号为001的用户的修改权限等,通过权限可知用户对哪些资源都有哪些操作许可.

RBAC

基于角色的访问控制

RBAC基于角色的访问控制是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

图片三
授权代码可表示为:

if(主体.hasRole("总经理角色id")) {
    
    
    查询工资
}

基于资源的访问控制

RBAC基于资源的访问控制是按资源进行授权,比如:用户必须具有查询工资的权限才可以查询员工工资信息等:

图片四

授权代码可表示为:

if(主体.hasPermission("查询工资权限标识")) {
    
    
    查询工资
} 

基于session认证方式

创建工程

创建maven工程,并且在pom.xml文件加入以下依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cehcloud</groupId>
    <artifactId>security</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>
</project>

Spring容器配置

在config包下定义ApplicationConfig.java,它对应web.xml中ContextLoadListener的配置

package com.cehcloud.cehc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @author Lenovo
 * @date 2020/8/18 21:25
 */
@Configuration
@ComponentScan(basePackages = "com.cehcloud.cehc"
                ,excludeFilters = {
    
    @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class ApplicationConfig {
    
    
    // 在此配置除了Controller的其它bean,比如:数据库、事务管理器、业务bean等
}

servletContext配置

本案例采用Servlet3.0无web.xml方式,config包下定义的WebConfig.java,它对应DispatcherServlet配置.

package com.cehcloud.cehc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

/**
 * @author Lenovo
 * @date 2020/8/18 21:29
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.cehcloud.cehc"
                ,includeFilters = {
    
    @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {
    
    

    @Bean
    public InternalResourceViewResolver viewResolver() {
    
    
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/view/");
        viewResolver.setSuffix(".html");
        return viewResolver;
    }
}

加载Spring容器

在init包下定义Spring容器初始化类SpringApplicationInitializer,此类实现WebApplicationInitializer接口,Spring容器启动时加载WebApplicationInitializer接口的所有实现类.

package com.cehcloud.cehc.init;

import com.cehcloud.cehc.config.ApplicationConfig;
import com.cehcloud.cehc.config.WebConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @author Lenovo
 * @date 2020/8/18 21:45
 */
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    

    /**
     * Spring容器,相当于加载applicationContext.xml
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
    
    
        return new Class[]{
    
    ApplicationConfig.class};
    }

    /**
     * servletContext,相当于加载springmvc.xml
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
    
    
        return new Class[]{
    
    WebConfig.class};
    }

    /**
     * url-mapping
     * @return
     */
    @Override
    protected String[] getServletMappings() {
    
    
        return new String[]{
    
    "/"};
    }
}

登录页面:

<%--
  Created by IntelliJ IDEA.
  User: Lenovo
  Date: 2020/8/19
  Time: 13:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="login" method="post">
    用户名:<input type = "text" name = "username"/><br>
    密码:<input type="password" name="password"/><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

在WebConfig中新增如下配置,将直接导入登录页面:

	@Override
    public void addViewControllers(ViewControllerRegistry registry) {
    
    
      registry.addViewController("/").setViewName("login");
    }

认证接口

用户进入认证页面,输入账号和密码,点击登录,进行身份验证.
(1)定义认证接口,此接口用于对传来的用户名、密码校验。

package com.cehcloud.cehc.service;

/**
 * @author Lenovo
 * @date 2020/8/19 14:25
 */

import com.cehcloud.cehc.model.AuthenticationRequest;
import com.cehcloud.cehc.model.UserDTO;

/**
 * 认证服务
 */
public interface AuthenticationService {
    
    

    /**
     * 用户认证
     * @param authenticationRequest 用户认证请求
     * @return 认证成功的用户信息
     */
    UserDTO authentication(AuthenticationRequest authenticationRequest);
}

认证请求结构:

package com.cehcloud.cehc.model;

import lombok.Data;

/**
 * @author Lenovo
 * @date 2020/8/19 14:27
 */
@Data
public class AuthenticationRequest {
    
    
    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;
}

认证成功后返回用户详细信息,也就是当前登录用户的信息:

package com.cehcloud.cehc.model;

import lombok.Data;

/**
 * @author Lenovo
 * @date 2020/8/19 14:27
 */
@Data
@AllArgsConstructor
public class UserDTO {
    
    

    private String id;
    private String username;
    private String password;
    private String fullName;
    private String mobile;
}

(2)认证实现类,根据用户名查找用户信息,并校验密码,这里模拟两个用户:

package com.cehcloud.cehc.service.impl;

import com.cehcloud.cehc.model.AuthenticationRequest;
import com.cehcloud.cehc.model.UserDTO;
import com.cehcloud.cehc.service.AuthenticationService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Lenovo
 * @date 2020/8/19 14:35
 */
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    
    

    @Override
    public UserDTO authentication(AuthenticationRequest authenticationRequest) {
    
    
        if (authenticationRequest == null
            || StringUtils.isEmpty(authenticationRequest.getUsername())
            || StringUtils.isEmpty(authenticationRequest.getPassword())) {
    
    
            throw new RuntimeException("账号或密码为空");
        }
        UserDTO userDTO = getUserDTO(authenticationRequest.getUsername());
        if (userDTO == null) {
    
    
            throw new  RuntimeException("查不到该用户");
        }
        if (!authenticationRequest.getPassword().equals(userDTO.getPassword())) {
    
    
            throw new RuntimeException("账号或者密码错误");
        }
        return userDTO;
    }

    /**
     * 模拟用户查询
     * @param username
     * @return
     */
    public UserDTO getUserDTO(String username) {
    
    
        return userDTOMap.get(username);
    }

    /**
     * 用户信息
     */
    private Map<String, UserDTO> userDTOMap = new HashMap<>();
    {
    
    
        userDTOMap.put("zhangsan", new UserDTO());
        userDTOMap.put("lisi", new UserDTO());
    }
}

(3)登录controller,对登录请求处理,它调用AuthenticationService完成认证并返回登录提示信息.

package com.cehcloud.cehc.controller;

import com.cehcloud.cehc.model.AuthenticationRequest;
import com.cehcloud.cehc.model.UserDTO;
import com.cehcloud.cehc.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Lenovo
 * @date 2020/8/19 14:54
 */
@RestController
public class LoginController {
    
    

    @Autowired
    AuthenticationService authenticationService;

    @RequestMapping(value = "/login", produces = "text/plain; charset=utf-8")
    public String login(AuthenticationRequest authenticationRequest) {
    
    
        UserDTO userDTO = authenticationService.authentication(authenticationRequest);
        return userDTO.getUsername() + "登录成功";
    }
}

实现会话功能

(1)增加会话功能

​ 首先在UserDTO中定义一个SESSION_USER_KEY,作为Session中存放登录用户信息的key.

public static final String SESSION_USER_KEY = “_user” ;
​ 然后修改LoginController,认证成功后,将用户信息放在当前会话.并增加用户登录方法,退出登录时session失效.

package com.cehcloud.cehc.controller;

import com.cehcloud.cehc.model.AuthenticationRequest;
import com.cehcloud.cehc.model.UserDTO;
import com.cehcloud.cehc.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
 * @author Lenovo
 * @date 2020/8/19 14:54
 */
@RestController
public class LoginController {
    
    

    @Autowired
    AuthenticationService authenticationService;

    @RequestMapping(value = "/login", produces = "text/plain; charset=utf-8")
    public String login(AuthenticationRequest authenticationRequest, HttpSession session) {
    
    
        UserDTO userDTO = authenticationService.authentication(authenticationRequest);
        // 存入session
        session.setAttribute(UserDTO.SESSION_USER_KEY, userDTO);
        return userDTO.getUsername() + "登录成功";
    }
    
    @GetMapping(value = "/logout", produces = "text/plain; charset=utf-8")
    public String logout(HttpSession session) {
    
    
        session.invalidate();
        return "退出成功";
    }
}

猜你喜欢

转载自blog.csdn.net/qq_44880095/article/details/112899338
今日推荐