Spirng-Security基础--基于SpringMVC的Session认证

1、Session认证

基于session认证的方式的流程是,用户认证成功后,在服务端生成用户相关的数据保存在session,而发送给客户端的session_id存放到cookie中去,这样用客户端请求时带上session_id就可以验证服务器是否存在session数据,以此完成用户的合法校验。当用户退出系统或session过期销毁时,客户端的session_id也就无效了。

HttpSession的相关操作API

方法 含义
HttpSession getSession(Boolean create) 获取当前HttpSession对象
void setAttribute(String name,Object value) 向session中存放对象
object getAttribute(String name) 从session中获取对象
void removeAttribute(String name) 移除session中的对象
void invalidate() 使HttpSession失效
String getId() 获取当前sessionID

2、创建工程

使用IDEA创建一个空的Maven项目,spring、springmvc配置都使用java类的配置方式。

2.1创建好的maven项目结构 如下

在这里插入图片描述

2.2导入相关依赖

注意一定要设置打包方式为war包,否则回来配置maven中使用tomcat运行不生效
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>org.example</groupId>
    <artifactId>day01_start</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>day01_start</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <!--<configuration>
                        <path>/shiro</path>
                        <port>8080</port>
                        <uriEncoding>UTF-8</uriEncoding>
                        <url>http://localhost:8080/shiro</url>
                        <server>Tomcat7</server>
                    </configuration>-->
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>

                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <encoding>utf-8</encoding>
                        <useDefaultDelimiters>true</useDefaultDelimiters>
                        <resources>
                            <resource>
                                <directory>src/main/resources</directory>
                                <filtering>true</filtering>
                                <includes>
                                    <include>**/*</include>
                                </includes>
                            </resource>
                            <resource>
                                <directory>src/main/java</directory>
                                <includes>
                                    <include>**/*.xml</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

2.3Spring容器配置

// 相当于spring的主配置文件 ContextApplication.xml
@Configuration
// 加载排除controller类的包,controller交给SpringMVC来加载
@ComponentScan(basePackages = "com.mcs.security",
        excludeFilters = {
    
    @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class ApplicationConfig {
    
    
    // 在此配置除了Controller的其他bean,比如:数据库连接池、事务管理器、业务Bean
}

2.4servletContxt配置

// SpringMVC的主配置文件,相当于springmvc.xml
@Configuration
@EnableWebMvc
// 只加载controller下的包
@ComponentScan(basePackages = "com.mcs.security"
            ,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/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

2.5加载spring和springmvc配置文件

此文件相当于web.xml文件

public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    
    // 加载spring容器,相当于加载ApplicationContext.xml
    @Override
    protected Class<?>[] getRootConfigClasses() {
    
    
        return new Class<?>[] {
    
     ApplicationConfig.class };
    }

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

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

2.6实现登录认证功能

在webapp/WEB-INF/views下定义认证页面login.jsp

<%@ 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>

同时将根路径url配置绑定到登录页面,在WebConfig.java中配置

@Override
    public void addViewControllers(ViewControllerRegistry registry) {
    
    
        // 将/直接重定向到login页面
        registry.addViewController("/").setViewName("login");
    }

2.7配置maven中的tomcat并测试

在这里插入图片描述
在这里插入图片描述

3、登录认证功能

登录无非就是效验用户名和密码是否对应,先不连接数据库,模拟数据进行验证。

3.1 当前用户对象和用户信息对象

AuthenticationRequest.java用来封装提交的表单

@Data 
// 认证请求结构
public class AuthenticationRequest {
    
    
    private String username;
    private String password;
}

用户基本信息

@Data // 构造所有参数的getAndSet方法
@AllArgsConstructor // 拥有所有参数的构造方法
// 当前登录的用户信息
public class UserDto {
    
    
    public static final String SESSION_USER_KEY = "_user";

    private String id;
    private String username;
    private String password;
    private String fullname;
    private String mobile;

    // 权限信息
    private Set<String> authorities;
}

3.2 登录认证请求处理接口及实现类

@Service
public interface AuthenticationService {
    
    

    UserDto authentication(AuthenticationRequest authenticationRequest);
}

模拟数据,查询数据并验证

@Service
public class AuthenticationServiceImpl implements AuthenticationService{
    
    
    // 模拟数据库用户信息
    private Map<String, UserDto> userDtoMap = new HashMap<>();
    {
    
    
        Set<String> zs = new HashSet<>();
        zs.add("p1");
        Set<String> ls = new HashSet<>();
        ls.add("p2");
        userDtoMap.put("zhangsan", new UserDto("1010", "zhangsan", "123", "张三", "12345", zs));
        userDtoMap.put("lisi", new UserDto("1011", "lisi", "123", "张三", "12345", ls));
    }
    // 模拟数据库查询功能
    public UserDto getUserDto(String name) {
    
    
        UserDto userDto = userDtoMap.get(name);
        return userDto;
    }

    @Override
    public UserDto authentication(AuthenticationRequest authenticationRequest) {
    
    
        if (authenticationRequest.getPassword() == null ||
                    authenticationRequest.getPassword() == null) {
    
    
            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;
    }
}

3.3 controller进行请求控制

@Controller
@ResponseBody
public class loginController {
    
    

    @Autowired
    AuthenticationService authenticationService;
    // produces设置返回类型为文本类型,同时指定字符集
    @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.getFullname() + "登录成功";
    }
}

3.4运行测试登录功能

在这里插入图片描述

4、实现会话功能

我们在登录成功后保存了session信息,现在我们模拟两个资源,让登录过的用户访问,并显示出是谁访问哪个资源。

在controller配置类中模拟两个资源请求,另外配置退出登录。

    @RequestMapping(value = "/r/r1", produces = {
    
    "text/plain;charset=UTF-8"})
    public String test(HttpSession session) {
    
    
        String fullname = null;
        Object object = session.getAttribute(UserDto.SESSION_USER_KEY);
        if (null == object) {
    
    
            fullname = "匿名";
        } else {
    
    
            fullname = ((UserDto)object).getFullname();
        }
        return fullname + "访问资源r1";
    }

    @RequestMapping(value = "/r/r2", produces = {
    
    "text/plain;charset=UTF-8"})
    public String r2(HttpSession session) {
    
    
        String fullname = null;
        Object object = session.getAttribute(UserDto.SESSION_USER_KEY);
        if (null == object) {
    
    
            fullname = "匿名";
        } else {
    
    
            fullname = ((UserDto)object).getFullname();
        }
        return fullname + "访问资源r2";
    }

    @RequestMapping(value = "/logout", produces = {
    
    "text/plain;charset=UTF-8"})
    public String logout(HttpSession session) {
    
    
        // 使session失效
        session.invalidate();
        return "退出成功";
    }

测试
在这里插入图片描述
已经登录了则显示登录者访问了资源,没人就显示匿名者访问资源

5、实现授权功能

授权功能,就是不同的人访问不同的资源,普通用户不能访问到管理员的资源,因为没这个权限,我们设置用户的权限,并在访问时判断有无权限访问就是授权的实现。

在模拟用户数据时,已经初始化了用户权限相关信息。
在这里插入图片描述
怎么进行用户在访问时判断其有没有权限呢,我们使用springmvc的拦截器来拦截请求并判断有无权限,我们已经设置好的是:张三有访问r1的权限,李四有访问r2的权限,接下来看怎么实现的。

在interceptor包下定义SimpleAuthenticationInterceptor拦截器,实现授权拦截:
1、校验用户是否登录
2、校验用户是否拥有操作权限

@Component
public class SimpleAuthenticationInterceptor implements HandlerInterceptor {
    
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        // 读取会话信息
        Object object = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);
        if(object == null) {
    
    
            writeContent(response, "请登录");
        }
        UserDto userDto = (UserDto)object;
        // 获取请求得url
        String requestRUL = request.getRequestURI();
        if (userDto.getAuthorities().contains("p1") && requestRUL.contains("/r1")) {
    
    
            return true;
        }
        if (userDto.getAuthorities().contains("p2") && requestRUL.contains("/r2")) {
    
    
            return true;
        }
        writeContent(response, "权限不足,拒绝访问");
        return false;
    }

    // 响应数据给客户端
    private void writeContent(HttpServletResponse response, String msg) throws Exception{
    
    
        response.setContentType("text/html;charset=utf-8");
        // getWriter获取一个输出流
        PrintWriter writer = response.getWriter();
        // 将数据打印再客户端
        writer.println(msg);
        writer.close();
    }
}

拦截器配置好了,我们还需要在springmvc的配置文件中WebConfig.java配置一下拦截器

  // 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
    // 只拦截了/r/下的请求
        registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");
    }

测试
1、未登录状态
在这里插入图片描述
2、张三访问资源r1
在这里插入图片描述
3、张三访问资源r2
在这里插入图片描述
李四同样的就不再展示了

总结

我们是使用了springmvc的拦截器实现了用户的请求的拦截并模拟权限的授权,使用第三方安全框架会更加容易实现,下一章节是spring security框架的快速上手。

猜你喜欢

转载自blog.csdn.net/qq_44660367/article/details/109515421