SpringMVC基于session实现用户认证、会话、授权

认证

先搭建基本的项目框架,本项目不使用配置文件,全部使用配置类配置springmvc,之前还没有接触过,这个项目也算是补充了:

pom

<?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">
    <parent>
        <artifactId>testProject</artifactId>
        <groupId>org.rhett</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring.version>5.2.3.RELEASE</spring.version>
    </properties>
    <packaging>war</packaging>

    <artifactId>security-sprimgmvc</artifactId>
    <dependencies>
        <!-- spring & spring mvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>{spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>{spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- 工具 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>security‐springmvc</finalName>
        <pluginManagement>
            <plugins>
                <!-- tomcat插件启动 -->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                </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>
                    <version>3.1.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

配置类

@Configuration
@ComponentScan(basePackages = "com.rhett.security",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
//相当于application.xml
public class ApplicationConfig {

}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.rhett.security",
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
//相当于web.xml,实现WebMvcConfigurer
public class WebConfig implements WebMvcConfigurer {

    //视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/view/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

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

Spring容器初始化:这个类实现自WebApplicationInitializer,spring容器启动的时候会加载WebApplicationInitializer接口的所有实现类。

public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    //spring容器,相当于加载application.xml
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{ApplicationConfig.class};
    }
    //servletContext,相当于加载web.xml
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    //url-mapping
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

实体类

用户信息:

@Data
@AllArgsConstructor
public class UserDto {
    private String id;
    private String username;
    private String password;
    private String fullname;
    private String mobile;
}

封装用户提交的登录参数:

@Data
public class AuthenticationRequest {
    private String username;
    private String password;
}

认证service

public interface AuthenticationService {
    UserDto authentication(AuthenticationRequest request);
}

实现:

@Service
public class AuthenticationServiceImpl implements AuthenticationService{

    private Map<String,UserDto> userMap = new HashMap<>();

    //模拟数据库数据
    {
        userMap.put("zhangsan",new UserDto("1010","zhangsan","123","张三"));
        userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553"));
    }


    @Override
    public UserDto authentication(AuthenticationRequest request) {
        //校验参数是否为空
        if(request == null
                || StringUtils.isEmpty(request.getUsername())
                || StringUtils.isEmpty(request.getPassword())){
            throw new RuntimeException("账号和密码为空");
        }
        //根据账号去查询数据库
        UserDto user = getUserDto(request.getUsername());
        if(user==null){
            throw new RuntimeException("查询不到该用户");
        }
        if(!request.getPassword().equals(user.getPassword())){
            throw new RuntimeException("账号或密码错误");
        }
        return user;
    }
    //假装这是一个查询数据库的方法
    private UserDto getUserDto(String userName){
        return userMap.get(userName);
    }
}

controller

@RestController
public class LoginController {
    @Autowired
    private AuthenticationService authenticationService;

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

视图

部署在webapp/WEB-INF/view/

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

现在就可以进行登录了。

会话

要实现会话,就是将登录信息写入session中,首先取一个session名,可以写在UserDto中:

@Data
@AllArgsConstructor
public class UserDto {
    //session名
    public static final String SESSION_USER_KEY = "_user";
    private String id;
    private String username;
    private String password;
    private String fullname;
    private String mobile;
}

在登录的controller获取session,并将用户信息存入session:

@PostMapping(value = "/login",produces = "text/plain;charset=utf-8")
public String login(AuthenticationRequest request, HttpSession session){
    UserDto userDto = authenticationService.authentication(request);
    //存入session
    session.setAttribute(UserDto.SESSION_USER_KEY,userDto);
    return userDto.getUsername()+"登录成功";
}

写一个资源验证一下:

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

先登录,再访问,可以看到用户信息被识别了:

SpringMVC基于session实现用户认证、会话、授权

授权

更改controller,新增一个资源r2:

@GetMapping(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(object == null){
        fullname =  "匿名";
    }else {
        UserDto userDto = (UserDto) object;
        fullname = userDto.getFullname();
    }
    return fullname+"访问资源r2";
}

给用户增加一个表示权限的字段:

@Data
@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;
}

更改service的初始化代码块:

{
    Set<String> authorities1 = new HashSet<>();
    authorities1.add("p1");//p1和/r/r1对应
    Set<String> authorities2 = new HashSet<>();
    authorities2.add("p2");//p2和/r/r2对应
    userMap.put("zhangsan",new UserDto("1010","zhangsan","123","张三","133443",authorities1));
    userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553",authorities2));
}

写一个拦截器用于权限验证:

@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;
        //请求的uri
        String requestURI = request.getRequestURI();
        if(userDto.getAuthorities().contains("p1")&&requestURI.contains("r/r1")){
            return true;
        }
        if(userDto.getAuthorities().contains("p2")&&requestURI.contains("r/r2")){
            return true;
        }
        writeContent(response,"没有权限,拒绝访问");
        return false;
    }

    private void writeContent(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(msg);
        writer.close();
    }
}

在WebConfig中配置拦截器:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.rhett.security",
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    SimpleAuthenticationInterceptor simpleAuthenticationInterceptor;

    //视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/view/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

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

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

验证:登录zhangsan,访问r1正常,访问r2提示权限不够

SpringMVC基于session实现用户认证、会话、授权

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/springmvc%e5%9f%ba%e4%ba%8esession%e5%ae%9e%e7%8e%b0%e7%94%a8%e6%88%b7%e8%ae%a4%e8%af%81%e3%80%81%e4%bc%9a%e8%af%9d%e3%80%81%e6%8e%88%e6%9d%83/