七台河新闻:【认证与授权】2、基于session的认证方式

admin/2020-04-27/ 分类:科技/阅读:

这一篇将通过一个简朴的web项目实现基于Session的认证授权方式,也是以往传统项目的做法。
先来温习一下流程

用户认证通过以后,在服务端天生用户相关的数据保留在当前会话(Session)中,发给客户端的数据将通过session_id 存放在cookie中。在后续的请求操作中,客户端将带上session_id,服务端就可以验证是否存在了,并可拿到其中的数据校验其正当性。当用户退出系统或session_id到期时,服务端则会销毁session_id。详细可查看上篇的基本观点领会。

1. 建立工程

本案例为了利便,直接使用springboot快速建立一个web工程

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"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>simple-mvc</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies> </project> 

1.2 实现认证功效

实现认证功效,我们一样平常需要这样几个资源

  • 认证的入口(认证页面)
  • 认证的凭证(用户的凭证信息)
  • 认证逻辑(若何才算认证乐成)

认证页面
也就是我们常说的登录页

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Login</title> </head> <body> <form th:action="@{/login}" method="post"> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><input type="submit" value="登录"/></div> </form> </body> </html> 

页面控制器
现在有了认证页面,那我若是才可以进入到认证页面呢,同时我点击上岸后,下一步该做什么呢?

@Controller public class LoginController { // 认证逻辑处置 @Autowired private AuthenticationService authenticationService; // 根路径直接跳转至认证页面 @RequestMapping("/") public String loginUrl() { return "/login"; } // 认证请求 @RequestMapping("/login") @ResponseBody public String login(HttpServletRequest request) { AuthenticationRequest authenticationRequest = new AuthenticationRequest(request); User user = authenticationService.authentication(authenticationRequest); return user.getUsername() "你好!"; } } 

通过客户端通报来的参数举行处置

public class AuthenticationRequest { private String username; private String password; public AuthenticationRequest(HttpServletRequest request){ username = request.getParameter("username"); password = request.getParameter("password"); } // 省略 setter getter } 

同时我们还需要一个状态用户信息的工具User

public class User { private Integer userId; private String username; private String password; private boolean enable; public User(Integer userId, String username, String password, boolean enable) { this.userId = userId; this.username = username; this.password = password; this.enable = enable; } // 省略 setter getter } 

有了用户了,有了入口了,接下来就是对这些数据的处置,看是否若何认证条件了

@Service public class AuthenticationService{ // 模拟数据库中保留的两个用户 private static final Map<String, User> userMap = new HashMap<String, User>() {{ put("admin", new User(1, "admin", "admin", true)); put("spring", new User(2, "spring", "spring", false)); }}; private User loginByUserName(String userName) { return userMap.get(userName); } @Override public User authentication(AuthenticationRequest authenticationRequest) { if (authenticationRequest == null || StringUtils.isEmpty(authenticationRequest.getUsername()) || StringUtils.isEmpty(authenticationRequest.getPassword())) { throw new RuntimeException("账号或密码为空"); } User user = loginByUserName(authenticationRequest.getUsername()); if (user == null) { throw new RuntimeException("用户不存在"); } if(!authenticationRequest.getPassword().equals(user.getPassword())){ throw new RuntimeException("密码错误"); } if (!user.isEnable()){ throw new RuntimeException("该账户已被禁用"); } return user; } } 

这里我们模拟了两个用户,一个是正常使用的账号,另有个账号由于某些特殊的缘故原由被封禁了,我们一起来测试一下。

启动项目在客户端输入localhost:8080 会直接跳转到认证页面

我们划分实验差别的账户密码登录看详细显示什么信息。

1、数据的密码不准确

2、账户被禁用

3、数据准确的用户名和密码

此时我们的测试均已相符预期,能够将准确的信息反馈给用户。这也是最基础的认证功效,用户能够通过系统的认证,说明他是该系统的正当用户,然则用户在后续的接见历程中,我们需要知道到底是哪个用户在操作呢,这时我们就需要引入到会话的功效呢。

1.3 实现会话功效

会话是指一个终端用户与交互系统举行通讯的历程,比如从输入账户密码进入操作系统到退出操作系统就是一个会话历程。
1、增添会话的控制

关于session的操作,可参考HttpServletRqeust的相关API

前面引言中我们提到了session_id的观点,与客户端的交互。
界说一个常量作为存放用户信息的key,同时在登录乐成后保留用户信息

privata finl static String USER_SESSION_KEY = "user_session_key"; @RequestMapping("/login") @ResponseBody public String login(HttpServletRequest request) { AuthenticationRequest authenticationRequest = new AuthenticationRequest(request); User user = authenticationService.authentication(authenticationRequest); request.getSession().setAttribute(USER_SESSION_KEY,user); return user.getUsername() "你好!"; } 

2、测试会话的效果

既然说用户认证后,我们将用户的信息保留在了服务端中,那我们就测试一下通过会话,服务端是否知道后续的操作是哪个用户呢?我们添加一个获取用户信息的接口 /getUser,看是否能后查询到当前登录的用户信息

@ResponseBody @RequestMapping("/getUser") public String getUser(HttpServletRequest request){ Object object = request.getSession().getAttribute("user_"); if (object != null){ User user = (User) object; return "当前接见用户为:" user.getUsername(); } return "匿名用户接见"; } 

我们通过客户端通报的信息,在服务端查询是否有用户信息,若是没有则是匿名用户的接见,若是有则返回该用户信息。

首先在不登录下直接接见localhost:8080/getUser 返回匿名用户接见

上岸后再接见返回当前接见用户为:admin

此时我们已经可以看到当认证通事后,后续的接见服务端通过会话机制将知道当前接见的用户是说,这将便于我们进一步处置对用户和资源的控制。

1.4 实现授权功效

既然我们知道了是谁在接见用户,接下来我们将对用户接见的资源举行控制。

  • 匿名用户针对部门接口不能接见,提醒其认证后再接见
  • 凭据用户拥有的权限对资源举行操作(资源查询/资源更新)

1、实现匿名用户不能接见。

前面我们已经可以通过/getUser的接口示例中知道是否是匿名用户,那接下来我们就对匿名用户举行阻挡后跳转到认证页面。

public class NoAuthenticationInterceptor extends HandlerInterceptorAdapter { private final static String USER_SESSION_KEY = "user_session_key"; // 前置阻挡,在接口接见前处置 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object attribute = request.getSession().getAttribute(USER_SESSION_KEY); if (attribute == null){ // 匿名接见 跳转到根路径下的login.html response.sendRedirect("/"); return false; } return true; } } 

然后再将自界说的匿名用户阻挡器,放入到web容器中使其生效

@Configuration public class WebSecurityConfig implements WebMvcConfigurer { // 添加自界说阻挡器,珍爱路径/protect 下的所有接口资源 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new NoAuthenticationInterceptor()).addPathPatterns("/protect/**"); } } 

我们珍爱/protect 下的所有接口资源,当匿名用户接见上述接口时,都将被系统跳转到认证页面举行认证后才可以接见。

@ResponseBody @RequestMapping("/protect/getResource") public String protectResource(HttpServletRequest request){ return "这是非匿名用户接见的资源"; } 

这里我们就不纵情测试页面的展示了。

2、凭据用户拥有的权限对资源举行操作(资源查询/资源更新)

凭据匿名用户处置的方式,我们此时也可设置阻挡器,对接口的权限和用户的权限举行对比,通事后放行,不通过则提醒。此时我们需要设置这样几个地方

  • 用户所具有的权限
  • 一个权限对比的阻挡器
  • 一个资源接口

革新用户信息,使其具有响应的权限

public class User { private Integer userId; private String username; private String password; private boolean enable; // 授予权限 private Set<String> authorities; public User(Integer userId, String username, String password, boolean enable,Set<String> authorities) { this.userId = userId; this.username = username; this.password = password; this.enable = enable; this.authorities = authorities; } } 

重新设置用户

private static final Map<String, User> userMap = new HashMap<String, User>() {{ Set<String> all =new HashSet<>(); all.add("read"); all.add("update"); Set<String> read = new HashSet<>(); read.add("read"); put("admin", new User(1, "admin", "admin", true,all)); put("spring", new User(2, "spring", "spring", false,read)); }}; 

我们将admin用户设置最高权限,具有readupdate操作,spring用户只具有read权限

权限阻挡器

public class AuthenticationInterceptor extends HandlerInterceptorAdapter { private final static String USER_SESSION_KEY = "user_session_key"; // 前置阻挡,在接口接见前处置 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object attribute = request.getSession().getAttribute(USER_SESSION_KEY); if (attribute == null) { writeContent(response,"匿名用户不能接见"); return false; } else { User user = ((User) attribute); String requestURI = request.getRequestURI(); if (user.getAuthorities().contains("read") && requestURI.contains("read")) { return true; } if (user.getAuthorities().contains("update") && requestURI.contains("update")) { return true; } writeContent(response,"权限不足"); return false; } } //响应输出 private void writeContent(HttpServletResponse response, String msg) throws IOException { response.setContentType("text/html;charset=utf 
TAG:
阅读:
Sunbet_进入申博sunbet官网
微信二维码扫一扫
关注微信公众号
新闻自媒体 Copyright © 2002-2019 Sunbet 版权所有
二维码
意见反馈 二维码