下面来通过一个实例讲解Session认证的方式
创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站设计、网站制作、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的青山网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
创建工程:
引入依赖:
org.springframework spring-webmvc 5.0.4.RELEASE javax.servlet javax.servlet-api 3.1.0 org.projectlombok lombok 1.18.8 security‐springmvc org.apache.tomcat.maven tomcat7‐maven‐plugin 2.2 org.apache.maven.plugins maven‐compiler‐plugin 1.8 maven‐resources‐plugin utf‐8 true src/main/resources true **/* src/main/java **/*.xml
Spring容器配置
在config包下定义ApplicationConfig.java,这个配置类相当于spring的配置文件
@Configuration
@ComponentScan(basePackages = ,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.)})ApplicationConfig { }
在config包下定义WebConfig.java,这个配置类相当于springmv的配置文件
@Configuration @EnableWebMvc @ComponentScan(basePackages = "cn.xh" ,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(".jsp"); return viewResolver; } }
加载spring容器
在init包下定义spring容器的初始化类SpringApplicationInitializer,该类实现了WebApplicationInitializer接口相当于web.xml文件。Spring容器启动时会加载所有实现了WebApplicationInitializer接口的类。
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class>[] getRootConfigClasses() { return new Class>[] { ApplicationConfig.class }; } @ Override protected Class>[] getServletConfigClasses() { return new Class>[] { WebConfig.class }; } @ Override protected String[] getServletMappings() { return new String [] {"/"}; } }
该类对应的web.xml文件可以参考:
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
/WEB‐INF/application‐context.xml
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB‐INF/spring‐mvc.xml
1
springmvc
/
实现认证功能
在webapp/WEB-INF/views下定义认证页面login.jsp
<%@ pagecontentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>用户登录title>
head>
<body>
<formaction="login"method="post">
用户名:<inputtype="text"name="username"><br>
密 码:
<inputtype="password"name="password"><br>
<inputtype="submit"value="登录">
form>
body>
html>
在WebConfig中新增如下配置,将/直接导向login.jsp页面:
@Override
public voidaddViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
}
启动项目,访问/路径地址,进行测试
创建认证接口;
认证接口用来对传入的用户名和密码进行验证,验证成功返回用户的详细信息,失败抛出错误异常。
public interfaceAuthenticationService {
/**
*用户认证
* @paramauthenticationRequest用户认证请求
* @return认证成功的用户信息
*/
UserDto authentication(AuthenticationRequest authenticationRequest);
}
认证请求结构:
@Data
public classAuthenticationRequest {
/**
*用户名
*/
privateString username;
/**
*密码
*/
privateString password;
}
用户详细信息:
@Data
@AllArgsConstructor
public classUserDto {
privateString id;
privateString username;
privateString password;
privateString fullname;
privateString mobile;
}
认证实现类:
@Service
public classAuthenticationServiceImpl implementsAuthenticationService {
@Override
publicUserDto authentication(AuthenticationRequest authenticationRequest) {
if(authenticationRequest == null
|| StringUtils.isEmpty(authenticationRequest.getUsername())
|| StringUtils.isEmpty(authenticationRequest.getPassword())){
throw newRuntimeException("账号或密码为空");
}
UserDto userDto = getUserDto(authenticationRequest.getUsername());
if(userDto == null){
throw newRuntimeException("查询不到该用户");
}
if(!authenticationRequest.getPassword().equals(userDto.getPassword())){
throw newRuntimeException("账号或密码错误");
}
returnuserDto;
}
//模拟用户查询
publicUserDto getUserDto(String username){
returnuserMap.get(username);
}
//用户信息
privateMap
{
userMap.put("zhangsan",newUserDto("1010","zhangsan","123","张三","133443"));
userMap.put("lisi",newUserDto("1011","lisi","456","李四","144553"));
}
}
登录controller:
@RestController
public classLoginController {
@Autowired
privateAuthenticationService authenticationService;
/**
*用户登录
* @paramauthenticationRequest登录请求
* @return
*/
@PostMapping(value = "/login",produces = "text/plain;charset=utf‐8")
publicString login(AuthenticationRequest authenticationRequest){
UserDto userDto = authenticationService.authentication(authenticationRequest);
returnuserDto.getFullname() + "登录成功";
}
}
测试
实现会话功能:
当用户登录系统后,系统需要记住用户的信息,一般会把用户的信息放在session中,在需要的时候从session中获取用户的信息,这就是会话机制。
首先在UserDto中定义一个Session_USER_KEY,作为session的key
public static finalString SESSION_USER_KEY= "_user";
修改LoginController,认证成功后,将用户的信息放入session,并增加用户注销的方法,用户注销时清空session
@PostMapping(value = "/login",produces = "text/plain;charset=utf‐8")
publicString login(AuthenticationRequest authenticationRequest, HttpSession session){
UserDto userDto = authenticationService.authentication(authenticationRequest);
//用户信息存入session
session.setAttribute(UserDto.SESSION_USER_KEY,userDto);
returnuserDto.getUsername() + "登录成功";
}
@GetMapping(value = "logout",produces = "text/plain;charset=utf‐8")
publicString logout(HttpSession session){
session.invalidate();
return"退出成功";
}
增加测试资源,在LoginController中增加测试资源:
@GetMapping(value = "/r/r1",produces = {"text/plain;charset=utf-8"})
publicString r1(HttpSession session){
String fullname = null;
Object userObj = session.getAttribute(UserDto.SESSION_USER_KEY);
if(userObj != null){
fullname = ((UserDto)userObj).getFullname();
}else{
fullname = "匿名";
}
returnfullname + "访问资源1";
}
测试:
未登录访问 /r/r1显示:
已登录访问 /r/r1显示:
实现授权功能
用户访问系统需要经过授权,需要完成如下功能:
禁止未登录用户访问某些资源
登录用户根据用户的权限决定是否能访问某些资源
第一步:在UserDto里增加权限属性表示该登录用户拥有的权限:
@Data
@AllArgsConstructor
public classUserDto {
public static finalString SESSION_USER_KEY= "_user";
privateString id;
privateString username;
privateString password;
privateString fullname;
privateString mobile;
/**
*用户权限
*/
privateSet
}
第二步:在AuthenticationServiceImpl中为用户初始化权限,张三给了p1权限,李四给了p2权限:
//用户信息
privateMap
{
Set
authorities1.add("p1");
Set
authorities2.add("p2");
userMap.put("zhangsan",newUserDto("1010","zhangsan","123","张三","133443",authorities1));
userMap.put("lisi",newUserDto("1011","lisi","456","李四","144553",authorities2));
}
第三步:在LoginController中增加测试资源:
/**
*测试资源2
* @paramsession
* @return
*/
@GetMapping(value = "/r/r2",produces = {"text/plain;charset=utf-8"})
publicString r2(HttpSession session){
String fullname = null;
Object userObj = session.getAttribute(UserDto.SESSION_USER_KEY);
if(userObj != null){
fullname = ((UserDto)userObj).getFullname();
}else{
fullname = "匿名";
}
returnfullname + "访问资源2";
}
第四步:在interceptor包下实现授权拦截器SimpleAuthenticationInterceptor:
校验用户是否登录
校验用户是否有操作权限
@Component
public classSimpleAuthenticationInterceptor implementsHandlerInterceptor {
//请求拦截方法
@Override
public booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object
handler) throwsException {
//读取会话信息
Object object = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);
if(object == null) {
writeContent(response, "请登录");
}
UserDto user = (UserDto) object;
//请求的url
String requestURI = request.getRequestURI();
if(user.getAuthorities().contains("p1") && requestURI.contains("/r1")) {
return true;
}
if(user.getAuthorities().contains("p2") && requestURI.contains("/r2")) {
return true;
}
writeContent(response, "权限不足,拒绝访问");
return false;
}
//响应输出
private voidwriteContent(HttpServletResponse response, String msg) throwsIOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(msg);
writer.close();
response.resetBuffer();
}
}
在WebConfig中配置拦截器,配置/r/**的资源被拦截器处理:
@Autowired
privateSimpleAuthenticationInterceptor simpleAuthenticationInterceptor;
@Override
public voidaddInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");
}
测试:
未登录:
张三访问/r/r1:
张三访问 /r/r2: