权限验证
使用maven搭建一个springmvc 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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cc</groupId>
<artifactId>security-springmvc</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.targer>1.8</maven.compiler.targer>
</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.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- tomcat插件控制 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>
8000
</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
在java包下创建的包目录结构
编写ApplicationConfig类,相当于applicationContext.xml
@Configuration //就相当于applicationContext.xml文件
@ComponentScan(basePackages = "com.cc.securityspringmvc",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class ApplicationConfig {
//在此处配置了处理Controller的其他bean,比如:数据库连接池,事务管理器,业务bean等
}
编写WebConfig 类,相当于springmvc.xml
@Configuration //就相当于springmvc.xml文件
@EnableWebMvc
@ComponentScan(basePackages = "com.cc.securityspringmvc",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;
}
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("login");
}
}
编写SpringApplicationInitializer类,相当于web.xml
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//spring容器 相当于加载application.xml
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ApplicationConfig.class};
}
//WebApplicationInitializer
//servletContext 相当于加载springmvc.xml
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
//url-mapping
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
//这个文件相当于web.xml
创建login.jsp视图
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<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>
配置tomcat
启动tomcat,访问http://localhost:8080/securityspringmvc/
基于Session的认证方式
创建两个实体类,AuthenticationRequet,UserDto
@Data
public class AuthenticationRequet {
//请求认证参数,账号密码
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;
}
创建登录接口及实现类,AuthenticationService,AuthenticationServiceImpl
public interface AuthenticationService {
UserDto authentication(AuthenticationRequet authenticationRequet);
}
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
@Override
public UserDto authentication(AuthenticationRequet authenticationRequet) {
//校验参数是否为空
if (authenticationRequet == null || StringUtils.isEmpty(authenticationRequet.getUsername()) || StringUtils.isEmpty(authenticationRequet.getPassword())) {
throw new RuntimeException("账号和密码为空");
}
//根据账号和密码去查询数据库,这里采用模拟方法
UserDto user= getUserDto(authenticationRequet.getUsername());
//判断用户是否为空
if (user == null){
throw new RuntimeException("查询不到该用户");
}
//校验密码
if(!authenticationRequet.getPassword().equals(user.getPassword())){
throw new RuntimeException("账号或密码错误!");
}
//认证通过
return user;
}
//根据账号查询用户信息
private UserDto getUserDto(String userName){
return userMap.get(userName);
}
//用户信息
private Map<String,UserDto> userMap=new HashMap<>();
{
userMap.put("zhangsan",new UserDto("1010","zhangsan","123","张三","13345895"));
userMap.put("lisi",new UserDto("1011","lisi","123","李四","13345895"));
}
}
创建控制层登录
@RestController
public class LoginController {
@Autowired
AuthenticationService authenticationService;
//produces = "text/plain;charset=utf-8" 因为是在springmvc中,没用像SpringBoot这样的框架有封装,要指定,不然乱码
@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")
public String login(AuthenticationRequet authenticationRequet){
UserDto userDto = authenticationService.authentication(authenticationRequet);
return userDto.getUsername()+"登录成功";
}
}
启动项目进行登录
实现会化功能
在UserDto中添加如下代码,用于存放session
public static final String SESSION_USER_KEY="_user";
改造 LoginController
@RestController
public class LoginController {
@Autowired
AuthenticationService authenticationService;
@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8") //produces = "text/plain;charset=utf-8" 因为是在springmvc中,没用像SpringBoot这样的框架有封装,要指定,不然乱码
public String login(AuthenticationRequet authenticationRequet, HttpSession session){
UserDto userDto = authenticationService.authentication(authenticationRequet);
//存入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";
}
@GetMapping(value = "/logout",produces = {"text/plain;charset=UTF-8"})
public String logout(HttpSession session){
session.invalidate();
return "退出成功";
}
}
以上便实现登录的会话,主要就是个HttSession
基于Session的认证方式-实现授权功能
在 LoginController中在添加一个资源r/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";
}
在UserDto中再添加一个授权字段
//用户权限
private Set<String> authorities;
修改AuthenticationServiceImpl类,给这两个用户给权限
//用户信息
private Map<String,UserDto> userMap=new HashMap<>();
{
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","张三","13345895",authorities1));
userMap.put("lisi",new UserDto("1011","lisi","123","李四","13345895",authorities2));
}
编写一个拦截器使权限生效
/**
* 编写拦截器
*/
@Component
public class SimpleAuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//在这个方法中校验用户请求的url是否在用户的权限范围内
//取出用户的身份信息
Object objet = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);
if(objet == null){
//没有认证,提示登录
writeContent(response,"请登录");
}
UserDto userDto = (UserDto) objet;
//获取请求的url
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.print(msg);
writer.close();
response.resetBuffer(); //清理缓存
}
}
在WebConfig类中注入拦截器,使拦截器生效
//注入编写的拦截器使生效
@Autowired
SimpleAuthenticationInterceptor simpleAuthenticationInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//不添加addPathPatterns会拦截所有的url
registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");
}
启动项目访问:登录shangsan ,访问/r/r1有权限能正常访问,访问/r/r2,没有权限,提示:没有权限,拒绝访问!
快速搭建springsecurity的web环境
新建一个maven工程
<?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>com.cc</groupId>
<artifactId>springsecurity</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.targer>1.8</maven.compiler.targer>
</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.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- tomcat插件控制 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>
8000
</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建ApplicationConfig类
@Configuration //就相当于applicationContext.xml文件
@ComponentScan(basePackages = "com.cc.securityspringmvc",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class ApplicationConfig {
//在此处配置了处理Controller的其他bean,比如:数据库连接池,事务管理器,业务bean等
}
以上便是项目的快速搭建
创建springSecurity的登录
编写WebConfig类
@Configuration //就相当于springmvc.xml文件
@EnableWebMvc
@ComponentScan(basePackages = "com.cc.securityspringmvc",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;
}
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("login");
}
}
创建 SpringApplicationInitializer类
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//spring容器 相当于加载application.xml
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ApplicationConfig.class};
}
//WebApplicationInitializer
//servletContext 相当于加载springmvc.xml
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
//url-mapping
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
//这个文件相当于web.xml
自定 WebSecurityConfig类,该框架自带登录页面,session会话,退出登录页面
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//定义用户信息服务(查询用户信息)
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
manager.createUser(User.withUsername("lisi").password("123").authorities("p2").build());
return manager;
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/r/**").authenticated() //所有/r/**的请求必须认证通过
.anyRequest().permitAll() //除了/r/**,其它的请求可以访问
.and()
.formLogin() //允许表单登录
.successForwardUrl("/login-success"); //自定义登录成功的页面地址
}
}
将上面这类加载到spring容器,修改SpringApplicationInitializer的getRootConfigClasses()方法
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ApplicationConfig.class,WebSecurityConfig.class};
}
SpringSecurity初始化
SpringSecurity初始化,有两种,
(1)若当前没有使用Spring或SpringMVC,则需要将WebSecurityConfig传入超类,以确保获取配置,并创建Spring context容器
(2)相反,若当前环境已经使用Spring,我们应当在现有SpringContext中注册SpringSecurity(上一步已经将WebSecurityConfig加载至rootcontext),此方法可以什么都不做,在init包下定义SpirngSecurityApplicationInitializer.
public class SpringSecurityApplicationInitizlizer extends AbstractSecurityWebApplicationInitializer {
public SpringSecurityApplicationInitizlizer() {
//super(WebSecurityConfig.class);
}
}
编写控制类:
@RestController
public class LoginController {
@RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
public String loginSuccess(){
return "登录成功!";
}
@RequestMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
public String r1(){
return "访问资源1";
}
@RequestMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
public String r2(){
return "访问资源2";
}
}
修改WebConfig等方法addViewControllers让它启动时自动重定向到springsecurity框架自带的longin页面。
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("redirect:/login");
}
重启项目:登录页面
退出页面
springSecurity授权功能
在WebSecurityConfig类总修改 安全拦截机制模块的代码:
//安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
.antMatchers("/r/**").authenticated() //所有/r/**的请求必须认证通过
.anyRequest().permitAll() //除了/r/**,其它的请求可以访问
.and()
.formLogin() //允许表单登录
.successForwardUrl("/login-success"); //自定义登录成功的页面地址
}
访问r/r1必须要有p1的权限,r/r2/必须要有p2的权限。
SpringSecurity集成SpringBoot
工作目录
<?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>com.cc</groupId>
<artifactId>secutityspringboot</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</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.8</version>
</dependency>
</dependencies>
</project>
application.properties
server.port=8080
spring.application.name=security-springboot
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
@SpringBootApplication
public class SpirngSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpirngSecurityApplication.class,args);
}
}
@Configuration //就相当于springmvc.xml文件
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("redirect:/login");
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//定义用户信息服务(查询用户信息)
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
manager.createUser(User.withUsername("lisi").password("123").authorities("p2").build());
return manager;
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
.antMatchers("/r/**").authenticated() //所有/r/**的请求必须认证通过
.anyRequest().permitAll() //除了/r/**,其它的请求可以访问
.and()
.formLogin() //允许表单登录
.successForwardUrl("/login-success"); //自定义登录成功的页面地址
}
}
@RestController
public class LoginController {
@RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
public String loginSuccess(){
return "登录成功!";
}
@RequestMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
public String r1(){
return "访问资源1";
}
@RequestMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
public String r2(){
return "访问资源2";
}
}
启动项目访问http://localhost:8080