一、集成SpringBoot
1.1 集成SpringBoot
1.1.1 创建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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sun.security</groupId>
<artifactId>security-spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security-spring-boot</name>
<description>Demo project for Spring Boot</description>
<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>
<!-- 以下是>spring boot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 以下是>spring security依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 以下是jsp依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!--jsp页面使用jstl标签 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--用于编译jsp -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<build>
<finalName>security-springboot</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>utf-8</encoding>
<useDefaultDelimiters>true</useDefaultDelimiters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
1.1.2 编写配置文件
server.port=8080
server.servlet.context-path=/security-springboot
spring.application.name = security-springboot
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp
1.1.3 Servlet Context配置
/**
* 相当于springmvc.xml
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
//默认Url根路径跳转到/login,此url为spring security提供
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login");
}
}
1.1.4 安全配置
/**
* 安全配置(spring security)
*/
@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("456").authorities("p2").build());
return manager;
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/session/r1").hasAuthority("p1")
.antMatchers("/session/r2").hasAuthority("p2")
.antMatchers("/session/**").authenticated()//所有/session/**的请求必须认证通过
.anyRequest().permitAll()//除了/session/**,其他请求可以访问
.and()
.formLogin()//运行表单登录
.successForwardUrl("/login-success");//登录访问接口
}
}
1.2 工作原理
1.2.1 结构总览
Spring Security所解决的问题就是
安全访问控制
,而安全访问控制功能其实就是对所有进入系统的请求进行拦截,SpringMVC可以通过Filter或AOP等技术来实现,Spring Security对Web资源的保护是靠Filter实现的。
当初始化Spring Security时,会创建一个名为 SpringSecurityFilterChain 的Servlet过滤器,类型为
org.springframework.security.web.FilterChainProxy,它实现了javax.servlet.Filter,因此外部的请求会经过此类,下图是Spring Security过虑器链结构图:
FilterChainProxy是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器(AccessDecisionManager)进行处理,下图是FilterChainProxy相关类的UML图示。
spring Security功能的实现主要是由一系列过滤器链相互配合完成。
下面介绍过滤器链中主要的几个过滤器及其作用:
SecurityContextPersistenceFilter
这个Filter是整个拦截过程的入口和出口(也就是第一个和最后一个拦截器),会在请求开始时从配置好的 SecurityContextRepository 中获取 SecurityContext,然后把它设置给SecurityContextHolder。在请求完成后将 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同时清除 securityContextHolder 所持有的SecurityContext;
UsernamePasswordAuthenticationFilter
用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和
AuthenticationFailureHandler,这些都可以根据需求做相关改变;
FilterSecurityInterceptor
是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问,前面已经详细介绍过了;
ExceptionTranslationFilter
能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常:AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。
1.2.2 认证流程
让我们仔细分析认证过程:
- 用户提交用户名、密码被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 过滤器获取到,封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
- 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
- 认证成功后, AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除) Authentication 实例。
-
SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication ,通过
SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。
可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个List 列表,存放多种认证方式,最终实际的认证工作是由
AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为
DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最终
AuthenticationProvider将UserDetails填充至Authentication。
Authentication(认证信息)的结构:
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
(1)getAuthorities(),权限信息列表,默认是GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串。
(2)getCredentials(),凭证信息,用户输入的密码字符串,在认证过后通常会被移除,用于保障安全。
(3)getDetails(),细节信息,web应用中的实现接口通常为 WebAuthenticationDetails,它记录了访问者的ip地址和sessionId的值。
(4)getPrincipal(),身份信息,大部分情况下返回的是UserDetails接口的实现类,UserDetails代表用户的详细信息,那从Authentication中取出来的UserDetails就是当前登录用户信息,它也是框架中的常用接口之一。
1.2.3 自定义UserDetailsService
/**
* 自定义UserDetailsService查询
*/
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//登录账号
System.out.println("username="+username);
//根据账号去数据库查询
//这里暂时使用静态数据
UserDetails userDetails = User.withUsername(username).password("123").authorities("p1").build();
return userDetails;
}
}
去掉在WebSecurityConfig中定义的UserDetailsService从内存中拿取登录信息。
1.2.4 PasswordEncoder
1)采用字符串匹配方法,是明文的,输入密码是什么就拿什么密码对比
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
2)使用BCryptPasswordEncoder,需要对输入密码加密后对比
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
1.2.5 授权流程
- 拦截请求,已认证用户访问受保护的web资源将被SecurityFilterChain中的 FilterSecurityInterceptor 的子类拦截。
- 获取资源访问策略,FilterSecurityInterceptor会从SecurityMetadataSource 的子类DefaultFilterInvocationSecurityMetadataSource 获取要访问当前资源所需要的权限Collection 。
- 最后,FilterSecurityInterceptor会调用 AccessDecisionManager 进行授权决策,若决策通过,则允许访问资源,否则将禁止访问。
投票分析
public interface AccessDecisionVoter<S> {
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = ‐1;
boolean supports(ConfigAttribute var1);
boolean supports(Class<?> var1);
int vote(Authentication var1, S var2, Collection<ConfigAttribute> var3);
}
vote()方法的返回结果会是AccessDecisionVoter中定义的三个常量之一。ACCESS_GRANTED表示同意,ACCESS_DENIED表示拒绝,ACCESS_ABSTAIN表示弃权。如果一个AccessDecisionVoter不能判定当前
Authentication是否拥有访问对应受保护对象的权限,则其vote()方法的返回值应当为弃权ACCESS_ABSTAIN。
1.3 自定义认证
1.3.1 认证页面
在java同目录下新建webapp/WEB-INF/view/login.jsp
1.3.2 配置认证页面
在WebConfig.java中配置认证页面地址:
/**
* 相当于springmvc.xml
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
//默认Url根路径跳转到/login,此url为spring security提供
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/").setViewName("redirect:/login");//spring security提供的界面
//自定义页面
registry.addViewController("/").setViewName("redirect:/login-view");
registry.addViewController("/login-view").setViewName("login");
}
}
1.3.3 安全配置
在WebSecurityConfig中配置表章登录信息:
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/session/r1").hasAuthority("p1")
.antMatchers("/session/r2").hasAuthority("p2")
.antMatchers("/session/**").authenticated()//所有/session/**的请求必须认证通过
.anyRequest().permitAll()//除了/session/**,其他请求可以访问
.and()
.formLogin()//允许表单登录
.loginPage("/login-view")//登录页面
.loginProcessingUrl("/login")
.successForwardUrl("/login-success");//登录访问接口
}
1.3.4 连接数据库
1.3.4.1 创建数据库
创建user_db数据库
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL COMMENT '用户id',
`username` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`fullname` varchar(255) NOT NULL COMMENT '用户姓名',
`mobile` varchar(11) DEFAULT NULL COMMENT '手机号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC
1.3.4.2 在application.properties配置dataSource
spring.datasource.url=jdbc:mysql://localhost:3306/user_db
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver‐class‐name=com.mysql.jdbc.Driver
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql‐connector‐java</artifactId>
<version>5.1.47</version>
</dependency>
定义model
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
private String id;
private String username;
private String password;
private String fullname;
private String mobile;
}
定义dao
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//根据账号查询用户信息
public UserDto getUserDtoByUsername(String username){
String sql = "SELECT * FROM t_user where username = ?";
List<UserDto> userDtoList = jdbcTemplate.query(sql, new Object[]{username}, new BeanPropertyRowMapper<>(UserDto.class));
if(null != userDtoList && userDtoList.size() == 1){
return userDtoList.get(0);
}
return null;
}
}
定义service
/**
* 自定义UserDetailsService查询
*/
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查询用户
UserDto user = userDao.getUserDtoByUsername(username);
if(null == user){
//如果用户查不到,返回null,由provider来抛出异常
return null;
}
//根据用户的id查询用户的权限
List<String> permission = userDao.getPermissionByUserId(user.getId());
//将permission转成数组
String[] permissionArray = new String[permission.size()];
permission.toArray(permissionArray);
UserDetails userDetails = User.withUsername(user.getUsername()).password(user.getPassword()).authorities("p1").build();
return userDetails;
}
}
1.4 回话
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保存在会话中。spring security提供会话管理,认证通过后将身份信息放入SecurityContextHolder上下文,SecurityContext与当前线程进行绑定,方便获取用户身份。
1.4.1 获取用户身份
Spring Security获取当前登录用户信息的方法是SecurityContextHolder.getContext().getAuthentication()
//获取当前用户信息
private String getUername(){
String username = null;
//当前认证通过的用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//用户身份
Object principal = authentication.getPrincipal();
if(null == principal){
username = "匿名";
}
if(principal instanceof org.springframework.security.core.userdetails.UserDetails){
UserDetails userDetails = (UserDetails) principal;
username = userDetails.getUsername();
}else {
username = principal.toString();
}
return username;
}
1.4.2 回话控制
配置WebSecurityConfig
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//跨站请求伪造关闭
.authorizeRequests()
.antMatchers("/session/r1").hasAuthority("p1")
.antMatchers("/session/r2").hasAuthority("p2")
.antMatchers("/session/**").authenticated()//所有/session/**的请求必须认证通过
.anyRequest().permitAll()//除了/session/**,其他请求可以访问
.and()
.formLogin()//允许表单登录
.loginPage("/login-view")//登录页面
.loginProcessingUrl("/login")
.successForwardUrl("/login-success")//登录访问接口
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);//如果需要就创建一个Session(默认)登录时
}
always:如果没有session存在就创建一个
ifRequired:如果需要就创建一个Session(默认)登录时
never:SpringSecurity 将不会创建Session,但是如果应用中其他地方创建了Session,那么Spring
Security将会使用它。
stateless:SpringSecurity将绝对不会创建Session,也不使用Session
1.5 退出
配置WebSecurityConfig
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//跨站请求伪造关闭
.authorizeRequests()
// .antMatchers("/session/r1").hasAuthority("p1")
// .antMatchers("/session/r2").hasAuthority("p2")
.antMatchers("/session/**").authenticated()//所有/session/**的请求必须认证通过
.anyRequest().permitAll()//除了/session/**,其他请求可以访问
.and()
.formLogin()//允许表单登录
.loginPage("/login-view")//登录页面
.loginProcessingUrl("/login")
.successForwardUrl("/login-success")//登录访问接口
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)//如果需要就创建一个Session(默认)登录时
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login-view?logout");
}
当退出操作出发时,将发生:
- 使HTTP Session 无效
- 清除 SecurityContextHolder
- 跳转到 /login-view?logout
1.6 授权
1.6.1 授权分类
授权的方式包括 web授权和方法授权,web授权是通过 url拦截进行授权,方法授权是通过 方法拦截进行授权。他们都会调用accessDecisionManager进行授权决策,若为web授权则拦截器为FilterSecurityInterceptor;若为方法授权则拦截器为MethodSecurityInterceptor。如果同时通过web授权和方法授权则先执行web授权,再执行方法授权,最后决策通过,则允许访问资源,否则将禁止访问。
1.6.2 准备环境
1.6.2.1 数据库环境
在t_user数据库创建如下表:
角色表:
CREATE TABLE `t_role` (
`id` varchar(32) NOT NULL,
`role_name` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`status` char(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into `t_role`(`id`,`role_name`,`description`,`create_time`,`update_time`,`status`) values
('1','管理员',NULL,NULL,NULL,'');
用户角色关系表:
CREATE TABLE `t_user_role` (
`user_id` varchar(32) NOT NULL,
`role_id` varchar(32) NOT NULL,
`create_time` datetime DEFAULT NULL,
`creator` varchar(255) DEFAULT NULL,
PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into `t_user_role`(`user_id`,`role_id`,`create_time`,`creator`) values
('1','1',NULL,NULL);
权限表:
CREATE TABLE `t_permission` (
`id` varchar(32) NOT NULL,
`code` varchar(32) NOT NULL COMMENT '权限标识符',
`description` varchar(64) DEFAULT NULL COMMENT '描述',
`url` varchar(128) DEFAULT NULL COMMENT '请求地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into `t_permission`(`id`,`code`,`description`,`url`) values ('1','p1','测试资源
1','/session/r1'),('2','p3','测试资源2','/session/r2');
角色权限关系表:
CREATE TABLE `t_role_permission` (
`role_id` varchar(32) NOT NULL,
`permission_id` varchar(32) NOT NULL,
PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into `t_role_permission`(`role_id`,`permission_id`) values ('1','1'),('1','2');
1.6.3 添加dao、service
UserDao
//根据用户id查询用户权限
public List<String> getPermissionByUserId(String userId){
String sql = "select * from t_permission where id in(\n" +
"\tselect permission_id from t_role_permission where role_id in(\n" +
"\t\tselect role_id from t_user_role where user_id = ? \n" +
"\t)\n" +
")";
List<PermissionDto> list = jdbcTemplate.query(sql, new Object[]{userId}, new BeanPropertyRowMapper<>(PermissionDto.class));
List<String> permissionList = list.stream()
.map(PermissionDto::getCode)
.collect(Collectors.toList());
return permissionList;
}
修改SpringDataUserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查询用户
UserDto user = userDao.getUserDtoByUsername(username);
if(null == user){
//如果用户查不到,返回null,由provider来抛出异常
return null;
}
//根据用户的id查询用户的权限
List<String> permission = userDao.getPermissionByUserId(user.getId());
//将permission转成数组
String[] permissionArray = new String[permission.size()];
permission.toArray(permissionArray);
UserDetails userDetails = User.withUsername(user.getUsername()).password(user.getPassword()).authorities(permissionArray).build();
return userDetails;
}
权限model
/**
* 权限Dto
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PermissionDto {
private String id;
private String code;
private String description;
private String url;
}
1.6.4 web授权
配置WebSecurityConfig,注释掉权限分配,通过查询查询数据库赋予用户权限。
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//跨站请求伪造关闭
.authorizeRequests()
// .antMatchers("/session/r1").hasAuthority("p1")
// .antMatchers("/session/r2").hasAuthority("p2")
.antMatchers("/session/**").authenticated()//所有/session/**的请求必须认证通过
.anyRequest().permitAll()//除了/session/**,其他请求可以访问
.and()
.formLogin()//允许表单登录
.loginPage("/login-view")//登录页面
.loginProcessingUrl("/login")
.successForwardUrl("/login-success")//登录访问接口
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)//如果需要就创建一个Session(默认)登录时
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login-view?logout");
}
1.6.5 方法授权
授权有三类:@PreAuthorize,@PostAuthorize, @Secured
一般使用@PreAuthorize
在WebSecurityConfig中添加注解@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
/**
* 安全配置(spring security)
*/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
在controller方法中添加注解@PreAuthorize(“hasAuthority(‘p2’)”)
/**
* 测试资源2
* @return
*/
@GetMapping(value = "/session/r2",produces = {"text/plain;charset=UTF-8"})
@PreAuthorize("hasAuthority('p2')")
public String r2(){
return getUername()+" 访问资源2";
}