Spring Security二:Session的认证方式

下面来通过一个实例讲解Session认证的方式

创建工程

  Spring Security二:Session的认证方式_第1张图片

引入依赖:

<dependencies>

    <dependency>

        <groupId>org.springframeworkgroupId>

        <artifactId>spring-webmvcartifactId>

        <version>5.0.4.RELEASEversion>

    dependency>

    <dependency>

        <groupId>javax.servletgroupId>

        <artifactId>javax.servlet-apiartifactId>

        <version>3.1.0version>

    dependency>

    <dependency>

        <groupId>org.projectlombokgroupId>

        <artifactId>lombokartifactId>

        <version>1.18.8version>

    dependency>

dependencies>

<build>

    <finalName>security‐springmvcfinalName>

    <pluginManagement>

        <plugins>

            <plugin>

                <groupId>org.apache.tomcat.mavengroupId>

                <artifactId>tomcat7‐maven‐pluginartifactId>

                <version>2.2version>

            plugin>

            <plugin>

                <groupId>org.apache.maven.pluginsgroupId>

                <artifactId>maven‐compiler‐pluginartifactId>

                <configuration>

                    <source>1.8source>

                    <target>1.8target>

                configuration>

            plugin>

            <plugin>

                <artifactId>maven‐resources‐pluginartifactId>

                <configuration>

                    <encoding>utf‐8encoding>

                    <useDefaultDelimiters>trueuseDefaultDelimiters>

                    <resources>

                        <resource>

                            <directory>src/main/resourcesdirectory>

                            <filtering>truefiltering>

                            <includes>

                                <include>**/*include>

                            includes>

                        resource>

                        <resource>

                            <directory>src/main/javadirectory>

                            <includes>

                                <include>**/*.xmlinclude>

                            includes>

                        resource>

                    resources>

                configuration>

            plugin>

        plugins>

    pluginManagement>

build>

 

 

Spring容器配置

config包下定义ApplicationConfig.java,这个配置类相当于spring的配置文件

@Configuration 
@ComponentScan(basePackages = "cn.xh"

            ,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})

public class ApplicationConfig {

    //在此配置除了Controller的其它bean,比如:数据库链接池、事务管理器、业务bean等。

}

Springmvc配置

在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文件可以参考:
 
‐app>
‐class>org.springframework.web.context.ContextLoaderListener
‐param>
‐name>contextConfigLocation
‐value>/WEB‐INF/application‐context.xml
‐param>
‐name>springmvc
org.springframework.web.servlet.DispatcherServlet‐class>
‐param>
‐name>contextConfigLocation
‐value>/WEB‐INF/spring‐mvc.xml
‐param>
‐on‐startup>1
‐mapping>
‐name>springmvc
‐pattern>/
‐mapping>
‐app>
 

实现认证功能

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

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<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>

WebConfig中新增如下配置,将/直接导向login.jsp页面:

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

 

启动项目,访问/路径地址,进行测试

创建认证接口;

认证接口用来对传入的用户名和密码进行验证,验证成功返回用户的详细信息,失败抛出错误异常。

public interface AuthenticationService {
   
/**
     *
用户认证
     * @param
authenticationRequest 用户认证请求
     * @return 认证成功的用户信息
     */
   
UserDto authentication(AuthenticationRequest authenticationRequest);
}

 

认证请求结构:

@Data
public class AuthenticationRequest {
   
/**
     *
用户名
     */
   
private String username;
   
/**
     *
密码
     */
   
private String password;
}

 

用户详细信息:

@Data
@AllArgsConstructor

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

 

认证实现类:

@Service
public class AuthenticationServiceImpl implements AuthenticationService {
   
@Override
   
public UserDto authentication(AuthenticationRequest authenticationRequest) {
       
if(authenticationRequest == null
               
|| StringUtils.isEmpty(authenticationRequest.getUsername())
                || StringUtils.isEmpty(authenticationRequest.getPassword())){
           
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;
    }
   
//模拟用户查询
   
public UserDto getUserDto(String username){
       
return userMap.get(username);
    }
   
//用户信息
   
private Map userMap = new HashMap<>();
    {
       
userMap.put("zhangsan",new UserDto("1010","zhangsan","123","张三","133443"));
       
userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553"));
    }
}

 

登录controller:

@RestController
public class LoginController {
   
@Autowired
   
private AuthenticationService authenticationService;
   
/**
     *
用户登录
     * @param
authenticationRequest 登录请求
     * @return
    
*/
   
@PostMapping(value = "/login",produces = "text/plain;charset=utf‐8")   

public String login(AuthenticationRequest authenticationRequest){
        UserDto userDto =
authenticationService.authentication(authenticationRequest);
       
return userDto.getFullname() + " 登录成功";
    }
}

测试

 

 

实现会话功能:

当用户登录系统后,系统需要记住用户的信息,一般会把用户的信息放在session中,在需要的时候从session中获取用户的信息,这就是会话机制。

  1. 首先在UserDto中定义一个Session_USER_KEY,作为session的key

public static final String SESSION_USER_KEY = "_user";

  1. 修改LoginController,认证成功后,将用户的信息放入session,并增加用户注销的方法,用户注销时清空session

@PostMapping(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.getUsername() + "登录成功";
    }

   
@GetMapping(value = "logout",produces = "text/plain;charset=utf‐8")
   
public String logout(HttpSession session){
        session.invalidate();
       
return "退出成功";
    }

  1. 增加测试资源,在LoginController中增加测试资源:

 

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

  1. 测试:

未登录访问 /r/r1显示:

Spring Security二:Session的认证方式_第2张图片

已登录访问 /r/r1显示:

Spring Security二:Session的认证方式_第3张图片

 实现授权功能

用户访问系统需要经过授权,需要完成如下功能:

  1. 禁止未登录用户访问某些资源
  2. 登录用户根据用户的权限决定是否能访问某些资源

 

第一步:在UserDto里增加权限属性表示该登录用户拥有的权限:

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

第二步:在AuthenticationServiceImpl中为用户初始化权限,张三给了p1权限,李四给了p2权限:

//用户信息
private Map userMap = new HashMap<>();
{
    Set authorities1 =
new HashSet<>();
    authorities1.add(
"p1");
    Set authorities2 =
new HashSet<>();
    authorities2.add(
"p2");
   
userMap.put("zhangsan",new UserDto("1010","zhangsan","123","张三","133443",authorities1));
           
userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553",authorities2));

}

第三步:在LoginController中增加测试资源:

/**
 *
测试资源2
 * @param
session
 
* @return
 
*/

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

第四步:在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 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 void writeContent(HttpServletResponse response, String msg) throws IOException {
        response.setContentType(
"text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(msg);
        writer.close();
        response.resetBuffer();
    }
}

 

 

在WebConfig中配置拦截器,配置/r/**的资源被拦截器处理:

@Autowired
private SimpleAuthenticationInterceptor simpleAuthenticationInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(
simpleAuthenticationInterceptor).addPathPatterns("/r/**");
}

 

测试:

未登录:

Spring Security二:Session的认证方式_第4张图片

 

张三访问/r/r1:

Spring Security二:Session的认证方式_第5张图片

张三访问 /r/r2:

Spring Security二:Session的认证方式_第6张图片

我们通过session会话的方式实现了简单的认证和授权,但是在企业中我们往往会使用第三方的框架如:shiro和spring security来帮我们提高开发效率。

你可能感兴趣的:(java,spring,java,spring)