我们已经掌握了权限管理理论,核心就是用户认证和用户授权。那么我们就来学习springsecurity如何解决这两个问题。
springboot整合springsecurity比较简单,pom中引入直接使用starter相关的jar。
<!-- spring boot security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
<!-- 让html中能够使用security的标签 xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
这里重点注意一下springboot的版本号【1.5.4.RELEASE】;
因为我们还引用了thymeleaf-extras-springsecurity4版本【2.1.3.RELEASE】,用于页面权限管理
这是为了后面在html使用中springsecurity4的标签,如果版本冲突就无法生效
使用springsecurity只需要两步就可以了,
第一步,创建用户信息服务;
@Service
public class WebUserDetailsService implements UserDetailsService {
private final static Logger logger = LoggerFactory.getLogger(WebUserDetailsService.class);
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
logger.info("用户登录:"+username);
/**
* 这里直接创建UserDetails对象,
* 简化了用户信息数据库查询的逻辑,
* 让springsecurity研究更加专注框架本身
*/
if (StringUtils.isEmpty(username)) {
return null;
}else if("admin".equals(username) ){
//admin用户,密码123456
//roles("ADMIN","NORMAL"),角色不能加"ROLE_",security已经强制了。
UserDetails user = User.withUsername(username).password("123456").roles("ADMIN","NORMAL").build();
return user;
}else{
//普通用户,密码为123
UserDetails user = User.withUsername(username).password("123").roles("NORMAL").build();
return user;
}
}
}
重点注意:
这里直接返回了UserDetails 对象,没有查询数据库;
另外管理员和普通用户密码不一样;
roles角色字符串,已经不能用“ROLE_”前缀了。
最后UserDetailsService服务直接注册到spring中@Service就可以了
security会自动识别到自定义的服务
第二步,配置security的拦截信息;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
//URL权限控制,优先级从上到下
.authorizeRequests()
.antMatchers("/easyui/**","/**/*.js","/**/*.css","/**/*.png").permitAll() //无需授权的资源
.antMatchers("/test/admin/**").hasRole("ADMIN") //指定角色授权,直接使用hasRole方法
.antMatchers("/test/web/**").access("hasRole('ADMIN') or hasRole('NORMAL')") //指定角色授权,使用access方法
.anyRequest().authenticated() //默认所有资源都需要授权
//自定义登录逻辑
.and()
.formLogin()
.loginPage("/login.html") //自定义登录页面
.loginProcessingUrl("/login") //登录请求,默认url是/login
// .successForwardUrl("successForwardUrl") //登录成功跳转地址
// .failureForwardUrl("failureForwardUrl") //登录失败跳转地址
.permitAll() //允许登录相关的请求,所有人可以访问
//自定义注销逻辑
.and()
.logout()
// .logoutUrl("/my/logout") //默认url是/logout
.logoutSuccessUrl("/") //注销成功后,跳转指定页面
.invalidateHttpSession(true)
//关闭额外安全机制
.and()
.csrf().disable()//关闭csrf防跨站请求伪造机制,不然ajax访问无法使用
.headers().frameOptions().disable();//关闭iframe防攻击机制,不然iframe无法调用URL
}
}
重点注意:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
三个注解,后面用到方法限制权限,需要EnableGlobalMethodSecurity
HttpSecurity 配置大家可以自己看,比较简单,其他的功能也是这么配置,这个是使用security的方法。
security的整合配置就结束了,下面就是案例创建了。
我们自定义了登录页面,login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title> Web Demo</title>
</head>
<body >
<form action="/login" method="post" >
<p th:if="${param.logout}" >已注销</p>
<p th:if="${param.error}" >有错误,请重试</p>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</p>
<button type="submit" class="btn">Log in</button>
</form>
</body>
</html>
创建请求案例,TestController
@Controller()
public class TestController {
@RequestMapping("/login.html")
public String loginPage() {
return "auth/login";
}
/*================================
* 测试springsecurity的权限控制
* ================================
*/
/**
* 正则表达式配置拦截器权限控制,.antMatchers("/test/admin/**").hasRole("ADMIN")
*/
@RequestMapping("/admin/hello")
@ResponseBody
public String adminHello() {
return "hello admin!";
}
/**
* 提前配置拦截器权限控制,.antMatchers("/test/web/**").access("hasRole('ADMIN') or hasRole('NORMAL')")
*/
@RequestMapping("/web/{name}")
@ResponseBody
public String web(@PathVariable String name) {
return "hello web "+name+"!";
}
/**
* 方法名称上使用注解控制权限,@PreAuthorize("hasRole('ADMIN')")
*/
@RequestMapping("/only/admin")
@ResponseBody
@PreAuthorize("hasRole('ADMIN')")
public String authOnly() {
return "hello auth!";
}
/**
* html页面使用权限控制标签,<div sec:authorize="hasRole('ADMIN')">
*/
@RequestMapping("/auth")
public String auth() {
return "auth/success";
}
}
最后是html使用security的标签页面,success.html
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8" />
<title>Security Demo</title>
</head>
<body th:inline="text">
<div sec:authorize="isAuthenticated()">
<!-- 用户认证通过才能才显示 -->
<p>
用户名:<span sec:authentication="name"></span>
</p>
<p>
权限:<span sec:authentication="principal.authorities"></span>
</p>
</div>
<div sec:authorize="hasRole('ADMIN')">
<!-- 用户角色为“ADMIN”才显示 -->
<p>【管理员】才能看见的内容</p>
</div>
<div sec:authorize="hasRole('NORMAL')">
<!-- 用户角色为“NORMAL”才显示 -->
<p>【普通用户】才能看到的内容</p>
</div>
<form th:action="@{/logout}" method="post">
<input type="submit" value="注销" />
</form>
</body>
</html>
重点注意:
xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity4”
sec:authorize=“hasRole(‘NORMAL’)”
先要添加html的xmlns命名空间信息,然后才能使用sec标签
看看页面访问效果吧。
回顾总结,springboot整合springsecurity,只需要自定义UserDetailsService,然后在WebSecurityConfigurerAdapter中配置权限控制,就完成了。使用权限方式有4种,咱们这里用到了正则表达式、方法注解、html标签。配置信息有请求控制、用户登录、用户注销。
常用注解
@PreAuthorize, 在方法调用前,基于表达式计算结果来限制方法访问
@PostAuthorize,允许方法调用,但是如果表达式结果为fasle则抛出异常
@PostFilter,允许方法调用,但必须按表达式过滤方法结果。
@PreFilter,允许方法调用,但必须在进入方法前过滤输入值
@EnableWebSecurity,打开安全验证服务
@EnableGlobalMethodSecurity,全局方法安全
@Order,多个拦截器时,添加拦截优先级
@Secured,控制方法访问
补充下springsecurity常用的权限判定方法。