导入的坐标:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysqlartifactId>
<version>8.0.21version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
<version>3.0.4.RELEASEversion>
dependency>
dependencies>
application.yaml配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/student?characterEncoding=utf-8
username: root
password: 1511
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #选择为Druid数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
mybatis:
type-aliases-package: com.kuang.pojo
mapper-locations: classpath:com/kuang/mapper/*.xml
由于使用了Druid,所以还有个DruidConfig.java
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DruidDataSource druidDataSource(){
return new DruidDataSource();
}
}
以下是Mybatis相关的文件
UserMapper.java
@Mapper
@Repository
public interface UserMapper {
public List<SysUser> findAll();
public SysUser findById(@Param("id") Integer id);
public SysUser findByUsername(@Param("username") String username);
}
UserMapper.xml
<mapper namespace="com.kuang.mapper.UserMapper">
<select id="findAll" resultType="sysUser">
select id,username,password,perms,role from user
select>
<select id="findById" resultType="sysUser" parameterType="int">
select id,username,password,perms,role from user where id=#{id}
select>
<select id="findByUsername" resultType="sysUser" parameterType="String">
select id,username,password,perms,role from user where username=#{username}
select>
mapper>
以下是controller相关的文件
RouteController.java 控制页面跳转
@Controller
public class RouteController {
@RequestMapping({"/","index"})
public String index(){
return "index";
}
@RequestMapping("toLogin")
public String toLogin(){
return "views/login";
}
@GetMapping("/toAdmin")
@PreAuthorize("hasPermission('/user/add','user:add')")
public String toAdmin(){
return "views/user/add";
}
@RequestMapping("level1/{id}")
public String level1(@PathVariable("id") int id){
return "views/level1/"+id;
}
@RequestMapping("level2/{id}")
public String level2(@PathVariable("id") int id){
return "views/level2/"+id;
}
@RequestMapping("level3/{id}")
public String level3(@PathVariable("id") int id){
return "views/level3/"+id;
}
@RequestMapping("/user/add")
@PreAuthorize("hasPermission('/user/delete','user:add')")
public String add(){
return "views/user/add";
}
@RequestMapping("/user/update")
@PreAuthorize("hasPermission('/user/delete','user:update')")
public String update(){
return "views/user/update";
}
@RequestMapping("/user/delete")
@PreAuthorize("hasPermission('/user/delete','user:delete')")
public String delete(){
return "views/user/delete";
}
}
整合完springboot和mybatis最后进行测试,看看dao和controller是否能正常执行
1.SecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //该注解用于开启spring方法级安全,不需要可以直接去掉
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//实现了UserDetailsService接口的类 用户登录操作
@Autowired
private CustomUserDetailsService userDetailsService;
//自定义的permission类
@Autowired
private MyPermissionEvaluator myPermissionEvaluator;
//链式编程
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,但是功能页只有对应有权限的人才能访问
http.authorizeRequests()
//请求权限的规则
.antMatchers("/","/index").permitAll() //无需权限访问
.antMatchers("/level1/**").hasRole("ADMIN") //需要ADMIN权限
.antMatchers("/level2/**").hasRole("ROOT")
.antMatchers("/level3/**").hasRole("USER");
//没有权限 默认到登录页面,需要开启登录的页面
http.formLogin().loginPage("/toLogin")
.loginProcessingUrl("/login") //对应login.html页面form表单提交的url
.usernameParameter("username")//绑定对应登录页面form表单的用户名name属性
.passwordParameter("password");//绑定对应登录页面form表单的密码name属性
http.csrf().disable(); //关闭csrf功能 登录失败可能存在的原因
//注销 以及 注销成功后返回首页
http.logout().logoutSuccessUrl("/");
//开启记住我功能,自定义接收前端的参数
http.rememberMe().rememberMeParameter("remember");
//配置permission,不需要可以直接去掉
http.authorizeRequests().expressionHandler(defaultWebSecurityExpressionHandler());
}
//认证
//密码编码:passwordEncoder
//在Spring security 5.0+新增了很多加密方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//内存加载用户测试,这些数据正常应该从数据库中获取
/*auth.inMemoryAuthentication()
//加密方式
.passwordEncoder(new BCryptPasswordEncoder())
//给明文密码编码
.withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
//通过and() 来链接 添加多个用户
.and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");*/
auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(rawPassword);
}
});
}
@Override
public void configure(WebSecurity web) throws Exception {
// 设置拦截忽略文件夹,可以对静态资源放行
web.ignoring().antMatchers("/qinjiang/css/**","/qinjiang/js/**");
}
//配置permission 不需要可以直接去掉 不影响role认证
@Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler(){
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setPermissionEvaluator(myPermissionEvaluator);
return defaultWebSecurityExpressionHandler;
}
}
2.CustomUserDetailsService (自定义UserDetailsService接口实现类)
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities = new ArrayList<>();
// 从数据库中取出用户信息
SysUser user = userService.findByUsername(username);
// 判断用户是否存在
if(user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
//添加角色权限
List<String> roles = Arrays.asList(user.getRole().split(","));
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
//获取permission权限,不需要可以直接去掉
List<String> permsList = Arrays.asList(user.getPerms().split(","));
// 返回UserDetails的实现类User(org.springframework.security.core.userdetails.User包下的User)
//当前的SysUser是UserDetails扩展类
return new SysUser(user.getUsername(),user.getPassword(),authorities,permsList);
}
}
3.MyPermissionEvaluator
//自定义方法权限判断类 需要实现 PermissionEvaluator接口
@Component
public class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
System.out.println("getAuthorities当前用户所拥有的角色-->"+authentication.getAuthorities());
System.out.println("authentication.getDetails([RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D708D767A3E3B0DDFA00FDB42C3EDA1F])-->"+
authentication.getDetails());
System.out.println("authentication.getName() 获取当前用户名-->"+authentication.getName());
System.out.println("authentication.getCredentials()-->"+authentication.getCredentials());
System.out.println("authentication.getPrincipal()-->"+authentication.getPrincipal());
System.out.println("当前页面所需的permission->"+permission);
//获取当前用户的资源权限
SysUser sysUser = (SysUser) authentication.getPrincipal();
List<String> permsList = sysUser.getPermsList();
for (String s : permsList) {
//如果拥有当前url权限,则放行
if (s.equalsIgnoreCase(permission.toString())){
return true;
}
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
4.SysUser.java
需要注意的是:org.springframework.security.core.userdetails包下的User类 仅有下面的属性
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private static final Log logger = LogFactory.getLog(User.class);
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
如果需要扩展就像SysUser.java类那么做就行了(SysUser中扩展了permission权限的获取),如果不需要则在
CustomUserDetailsService类中loadUserByUsername方法直接返回spring提供的User实现类
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(user.getUsername(),user.getPassword(),authorities,permsList);
}
//UserDetails接口的扩展
public class SysUser implements UserDetails {
private int id;
private String username;
private String password;
private String perms;
private String role;
private Collection<GrantedAuthority> authorities;
private List<String> permsList;
public SysUser() {
}
@Override
public String toString() {
return "SysUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", perms='" + perms + '\'' +
", role='" + role + '\'' +
", authorities=" + authorities +
", permsList=" + permsList +
'}';
}
public void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}
public List<String> getPermsList() {
return permsList;
}
public void setPermsList(List<String> permsList) {
this.permsList = permsList;
}
public SysUser(String username, String password, Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
this.password = password;
this.username = username;
}
public SysUser(String username, String password, Collection<GrantedAuthority> authorities,List<String> permsList) {
this.authorities = authorities;
this.password = password;
this.username = username;
this.permsList = permsList;
}
public SysUser(int id, String username, String password, String perms, String role,Collection<GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.password = password;
this.perms = perms;
this.role = role;
this.authorities = authorities;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPerms() {
return perms;
}
public void setPerms(String perms) {
this.perms = perms;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
5 . index页面,配合Security-thymeleaf整合包
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>首页title>
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet">
head>
<body>
<div class="ui container">
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页a>
<div class="right menu">
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon">i> 登录
a>
div>
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名:<span sec:authentication="name">span>
a>
div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon">i> 注销
a>
div>
div>
div>
div>
<div class="ui segment" style="text-align: center">
<h3>Spring Security Study by 秦疆h3>
div>
<div>
<br>
<div class="ui three column stackable grid">
<div class="column" sec:authorize="hasRole('ADMIN')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon">i> Level-1-1a>div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon">i> Level-1-2a>div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon">i> Level-1-3a>div>
div>
div>
div>
div>
<div class="column" sec:authorize="hasRole('ROOT')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon">i> Level-2-1a>div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon">i> Level-2-2a>div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon">i> Level-2-3a>div>
div>
div>
div>
div>
<div class="column" sec:authorize="hasRole('USER')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon">i> Level-3-1a>div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon">i> Level-3-2a>div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon">i> Level-3-3a>div>
div>
div>
div>
div>
<div class="column" sec:authorize="isAuthenticated()">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">userh5>
<hr>
<div><a th:href="@{/user/add}"><i class="bullhorn icon">i> adda>div>
<div><a th:href="@{/user/update}"><i class="bullhorn icon">i> updatea>div>
<div><a th:href="@{/user/delete}"><i class="bullhorn icon">i> deletea>div>
div>
div>
div>
div>
div>
div>
div>
<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}">script>
<script th:src="@{/qinjiang/js/semantic.min.js}">script>
body>
html>
CustomUserDetailsService 返回spring提供的User类 和 自定义的SysUser类的区别
User
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//当前的SysUser是UserDetails扩展类
return new User(user.getUsername(),user.getPassword(),authorities);
}
}
控制台打印输出
authentication.getPrincipal()
–>org.springframework.security.core.userdetails.User
Username=admin,
Password=[PROTECTED],
Enabled=true,
AccountNonExpired=true,
credentialsNonExpired=true,
AccountNonLocked=true,
Granted Authorities=[ROLE_ADMIN, ROLE_ROOT]]
可以看出 只有这些属性是不够的,所有需要自定义SysUser实现类
SysUser
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//当前的SysUser是UserDetails扩展类
return new SysUser(user.getUsername(),user.getPassword(),authorities,permsList);
}
}
控制台打印输出:
authentication.getPrincipal()
–>
SysUser{id=0,
username=‘admin’,
password=‘123456’,
perms=‘null’,
role=‘null’,
authorities=[ROLE_ADMIN, ROLE_ROOT],
permsList=[user:add, user:update, user:delete]}
还需要其他数据时 我们还可以继续添加属性
@EnableGlobalMethodSecurity注解详解
当我们想要开启spring方法级安全时,只需要在任何 @Configuration实例上使用 @EnableGlobalMethodSecurity 注解就能达到此目的。
同时这个注解为我们提供了prePostEnabled 、securedEnabled 和 jsr250Enabled 三种不同的机制来实现同一种功能:
prePostEnabled :
prePostEnabled = true 会解锁 @PreAuthorize 和 @PostAuthorize 两个注解。从名字就可以看出@PreAuthorize 注解会在方法执行前进行验证,
而 @PostAuthorize 注解会在方法执行后进行验证。
Secured:
@Secured注解是用来定义业务方法的安全配置。在需要安全[角色/权限等]的方法上指定 @Secured,并且只有那些角色/权限的用户才可以调用该方法。
@Secured缺点(限制)就是不支持Spring EL表达式。不够灵活。并且指定的角色必须以ROLE_开头,不可省略。该注解功能要简单的多,
默认情况下只能基于角色(默认需要带前缀 ROLE_)集合来进行访问控制决策。
该注解的机制是只要其声明的角色集合(value)中包含当前用户持有的任一角色就可以访问。
也就是 用户的角色集合和 @Secured 注解的角色集合要存在非空的交集。
不支持使用 SpEL 表达式进行决策。
附上 spingsecurity中haspermission用法以及配置自定义PermissionEvaluator
某网盘的链接里面有这套模板
链接:https://pan.baidu.com/s/1K5ip9PTej9XW2yYHUrC_cg
提取码:1111
复制这段内容后打开百度网盘手机App,操作更方便哦