一:创建项目与简单实现
- 创建一个spring boot项目,倚赖如下:
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 创建一个简单的接口如下:
package com.example.springsecurity01.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Auther: 洺润Star
* @Date: 2019/9/19 17:37
* @Description:
*/
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "Hello";
}
}
-
访问 http://localhost:8080/hello出现登陆界面,用户名默认为user,密码每次随机生成,启动项目时日志当中会有打印,登陆成功后会返回我们想要的结果
-
要想自定义用户名和密码我们可以在application.properties中实现
#下面分别为用户名,密码,和用户的角色
spring.security.user.name=root
spring.security.user.password=root
spring.security.user.roles=admin
二:基于内存的认证
package com.example.springsecurity01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @Auther: 洺润Star
* @Date: 2019/9/19 18:00
* @Description: 基于内存的认证配置了两个用户,继承了WebSecurityConfigurerAdapter
*/
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder(){
//不对密码进行加密
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("admin")
.and()
.withUser("zhang").password("123").roles("user");
}
}
三:登陆表单详细配置
以下代码配置了三个用户,不同路径所需要用户的权限,登陆(登出)和及其执行成功后的逻辑处理(本处可返回数据,便于前后端分离开发)
package com.example.springsecurity01.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
//@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("root").password("123").roles("ADMIN", "DBA")
.and()
.withUser("admin").password("123").roles("ADMIN", "USER")
.and()
.withUser("sang").password("123").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//authorizeRequests()方法开启了HttpSecurity配置
http.authorizeRequests()
//以下六行配置了访问路径所需要的角色
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/user/**")
.access("hasAnyRole('ADMIN','USER')")
.antMatchers("/db/**")
.access("hasRole('ADMIN') and hasRole('DBA')")
//下面两行定义了其它url访问必须通过登陆才能访问
.anyRequest()
.authenticated()
.and()
.formLogin()
//登陆界面(自定义的界面)
//.loginPage("/login_page")
//登陆处理接口
.loginProcessingUrl("/login")
//认证所需的用户名和密码的参数名
.usernameParameter("name")
.passwordParameter("passwd")
//登陆成功的处理逻辑
.successHandler((req, resp, auth) -> {
Object principal = auth.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(200);
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", principal);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
})
// .successForwardUrl("/hello")
//登陆失败的处理逻辑
.failureHandler((req, resp, e) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(401);
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账户被锁定,登录失败!");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "账户名或密码输入错误,登录失败!");
} else if (e instanceof DisabledException) {
map.put("msg", "账户被禁用,登录失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账户已过期,登录失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码已过期,登录失败!");
} else {
map.put("msg", "登录失败!");
}
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
})
.permitAll()
//注销登陆配置
.and()
//开启注销登陆的配置
.logout()
//注销登陆请求的URL
.logoutUrl("/logout")
//是否清除个人身份认证信息
.clearAuthentication(true)
//是否使session失效
.invalidateHttpSession(true)
//可添加注销的操作,如cookie的清除
.addLogoutHandler((req, resp, auth) -> {
})
//注销成功之后的业务逻辑,可添加页面的跳转
.logoutSuccessHandler((req, resp, auth) -> resp.sendRedirect("/login_page"))
.and()
.csrf()
.disable();
}
}
添加Controller进行测试
package com.example.springsecurity01.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Auther: 洺润Star
* @Date: 2019/9/19 17:37
* @Description:
*/
@RestController
public class HelloController {
@GetMapping("/admin/hello")
public String admin() {
return "hello admin!";
}
@GetMapping("/admin/db/hello")
public String admin2() {
return "/admin/db/hello";
}
@GetMapping("/user/hello")
public String user() {
return "hello user!";
}
@GetMapping("/db/hello")
public String dba() {
return "hello dba!";
}
}
四:方法安全
首先要开启基于注解的安全配置
package com.example.springsecurity01.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
/**
* @Auther: 洺润Star
* @Date: 2019/9/20 14:34
* @Description:
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class WebSecurityConfig {
}
开启后即可在service层进行配置,添加注解后即可对方法的调用进行权限控制
package com.example.springsecurity01.service;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
/**
* @Auther: 洺润Star
* @Date: 2019/9/20 14:40
* @Description:
*/
@Service
public class MethodService {
//表示需要ADMIN权限(注意要加前缀ROLE_)
@Secured("ROLE_ADMIN")
public String admin() {
return "hello admin";
}
//需要ADMIN和DBA两种权限
@PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
public String dba() {
return "hello dba";
}
//表示需要ADMIN或DBA或USER
@PreAuthorize("hasAnyRole('ADMIN','DBA','USER')")
public String user() {
return "user";
}
}