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框架的快速上手。