spring-secrity动态(数据库)对用户 权限限定以及鉴权里写自定义目录标题
1、利用spring-security 进行静态权限登录
1 创建项目
2、导包
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<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-devtools</artifactId>
<!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖myboot项目的项目如果想要使用devtools,需要重新引入 -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.4.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>1.12.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.30</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>false</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
3、写配置文件
server:
port: 8888
servlet:
session:
timeout: 10s
cookie:
http-only: true
secure: false
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: a111111
driver-class-name: com.mysql.jdbc.Driver
freemarker:
cache: false # 缓存配置 开发阶段应该配置为false 因为经常会改
suffix: .html # 模版后缀名 默认为ftl
charset: UTF-8 # 文件编码
template-loader-path: classpath:/templates/
security:
loginType: JSON #secrity返回的类型
user:
name: admin
password: admin
#logging:
# config: classpath:log4j2-dev.xml
mybatis:
configuration:
mapUnderscoreToCamelCase: true
3、编写简单的测试静态文件
4、介绍一下文件的作用
首先看config下的accesslog 在这个项目中没有用 可以删掉 下一个是auth 这是自定义的两个类重写了SimpleUrlAuthenticationFailureHandler 分别代表登陆成功和登陆失败的处理办法。
代码主要在securityConfig里面定义 下面我们会详细讲解
exception包里面主要是定义了异常的处理结果以及自定义的返回结果这个可以根据自己来定义
customExpiredSessionStrategy 主要是来处理当多人登录的时候(设定只有一个人登录的时候)采取的策略
mywebMvcConfigure 主要是注意排除掉静态资源的路径,不然静态资源无法访问
securityConfig这个类就是主要的配置文件
package com.javaweb.demo.config;
import com.javaweb.demo.config.auth.MyAuthenticationFailureHandler;
import com.javaweb.demo.config.auth.MyAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Resource
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
/**
* spring security 总体配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //禁用跨站csrf攻击防御,后面的章节会专门讲解
.formLogin()
.loginPage("/login.html")//用户未登录时,访问任何资源都转跳到该路径,即登录页面
.loginProcessingUrl("/login")//登录表单form中action的地址,也就是处理认证请求的路径
.usernameParameter("uname") //和登录表单的账号参数一样,不修改的话默认是username
.passwordParameter("pword") //登录表单中密码参数一样,不修改的话默认是password
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
/** 不要配置defaultSuccessUrl和failureUrl,否则自定义handler将失效(就是MyAuthenticationSuccessHandler)。handler配置与URL配置只能二选一*/
//.defaultSuccessUrl("/index")//登录认证成功后默认转跳的路径
//.failureUrl("/login.html") //登录认证是被跳转页面
.and()
.authorizeRequests() //权限控制
.antMatchers("/login.html", "/login")
.permitAll()//不需要通过登录验证就可以被访问的资源路径
.antMatchers("/biz1", "/biz2") //需要对外暴露的资源路径
.hasAnyAuthority("ROLE_user", "ROLE_admin") //user角色和admin角色都可以访问
.antMatchers("/syslog", "/sysuser")
.hasAnyRole("admin") //admin角色可以访问 和下面的两段代码等价 他会去configure中去看有没有admin这个用户
//.antMatchers("/syslog").hasAuthority("sys:log")
//.antMatchers("/sysuser").hasAuthority("sys:user")
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)//代表Spring Security只会在需要时创建一个HttpSession
.sessionFixation().migrateSession()//每次登录验证用创建一个新的Httpsession 将旧的属性复制带新的session中
.maximumSessions(1) //只准许一个人登录
.maxSessionsPreventsLogin(false) //一台设备已登录 另一个浏览器再此登录会将第一个挤掉
.expiredSessionStrategy(new CustomExpiredSessionStrategy()); //采取的策略
}
/**
* 用户配置
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("123456"))
.roles("user")
.and()
.withUser("admin")
.password(passwordEncoder().encode("123456"))
//.authorities("sys:log","sys:user")
.roles("admin")
.and()
.passwordEncoder(passwordEncoder());//配置BCrypt加密
}
/**
* 密码编码器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 静态资源访问
* @param web
*/
@Override
public void configure(WebSecurity web) {
//将项目中静态资源路径开放出来
web.ignoring().antMatchers( "/css/**", "/fonts/**", "/img/**", "/js/**");
}
}
登陆成功之后 会进入登陆成功之后处理的方法里面 返回一个index
如果用admin登录 他具有查看里面的所有权限 可以看到这里面的四个页面 如果是以user 登录 那么他只能看/biz1", "/biz2"这两个页面
总结以及对源码分析:
上面两个图是登录认证的流程
2、spring-security动态(数据库)对用户权限以及鉴权
下面我们来说一下动态(数据库)获取用户拥有什么角色以及相应得权限 (导包和编写配置文件和上面一样)的过程以及源码讲解
首先客户端发起请求 经过认证过滤器usernamePasswordAuthenticationFilter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIsbxqYL-1624372922986)(C:\Users\陈文杰\AppData\Roaming\Typora\typora-user-images\image-20210622193718446.png)]
通过attemptAuthentication() 首先将密码和用户名生成token 然后获取AuthenticationManager对象调用方法进行认证 认证之后在进入DaoAuthenticationProvider 这个方法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ab97NLDl-1624372922988)(C:\Users\陈文杰\AppData\Roaming\Typora\typora-user-images\image-20210622194238329.png)]
通过这个方法获取认证用户的信息 我们重写了这个方法 通过这个方法( MyUserDetailsService 里面的方法)我们可以获取 myUserDetails(实现了userdetails) 即UserDetails 用户的信息 MyUserDetailsService 类主要是用来获取用户的信息(我们通过实现userDetais来存储)如下图
package com.javaweb.demo.service;
import com.javaweb.demo.dao.MyUserDetailsServiceMapper;
import com.javaweb.demo.entity.MyUserDetails;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MyUserDetailsService implements UserDetailsService {
@Resource
private MyUserDetailsServiceMapper myUserDetailsServiceMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//加载基础用户信息
MyUserDetails myUserDetails = myUserDetailsServiceMapper.findByUserName(username);
//加载用户角色列表
List<String> roleCodes = myUserDetailsServiceMapper.findRoleByUserName(username);
//通过用户角色列表加载用户的资源权限列表
List<String> authorties = myUserDetailsServiceMapper.findAuthorityByRoleCodes(roleCodes);
//角色是一个特殊的权限,ROLE_前缀
roleCodes = roleCodes.stream()
.map(rc -> "ROLE_" +rc)
.collect(Collectors.toList());
authorties.addAll(roleCodes);
//将list集合变成List<GrantedAuthority> 类型
myUserDetails.setAuthorities(
AuthorityUtils.commaSeparatedStringToAuthorityList(
String.join(",",authorties)
)
);
return myUserDetails;
}
}
然后我们就要进行鉴权 新建一个类 通过里面方法我们能判断当前用户是否都拥有访问某个资源的权限
package com.javaweb.demo.service;
import com.javaweb.demo.dao.MyRBACServiceMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Component("rabcService")
public class MyRBACService {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Resource
private MyRBACServiceMapper myRBACServiceMapper;
/**
* 判断某用户是否具有该request资源的访问权限
*/
public boolean hasPermission(HttpServletRequest request,
Authentication authentication){
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails){
String username = ((UserDetails)principal).getUsername();
//表示该用户所对应的所有的能够访问的资源
List<String> urls = myRBACServiceMapper.findUrlsByUserName(username);
String url2 = request.getRequestURI();//代表这次访问中的资源
//判断用户所对应的资源中是否有request对应的资源
boolean flag = urls.stream().anyMatch(
url -> antPathMatcher.match(url,request.getRequestURI())
);
return flag;
}
return false;
}
}
securityconfig
package com.javaweb.demo.config;
import com.javaweb.demo.config.auth.MyAuthenticationFailureHandler;
import com.javaweb.demo.config.auth.MyAuthenticationSuccessHandler;
import com.javaweb.demo.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Resource
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Autowired
private MyUserDetailsService myUserDetailsService;
/**
* spring security 总体配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //禁用跨站csrf攻击防御,后面的章节会专门讲解
.formLogin()
.loginPage("/login.html")//用户未登录时,访问任何资源都转跳到该路径,即登录页面
.loginProcessingUrl("/login")//登录表单form中action的地址,也就是处理认证请求的路径
.usernameParameter("uname") //登录表单的账号参数,不修改的话默认是username
.passwordParameter("pword") //登录表单中密码参数,不修改的话默认是password
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
/** 不要配置defaultSuccessUrl和failureUrl,否则自定义handler将失效。handler配置与URL配置只能二选一*/
//.defaultSuccessUrl("/index")//登录认证成功后默认转跳的路径
//.failureUrl("/login.html") //登录认证是被跳转页面
.and()
.authorizeRequests() //权限控制
.antMatchers("/login.html", "/login")
.permitAll()//不需要通过登录验证就可以被访问的资源路径
/*.antMatchers("/","/biz1", "/biz2") //需要对外暴露的资源路径
.hasAnyAuthority("ROLE_user", "ROLE_admin") //user角色和admin角色都可以访问
.antMatchers("/syslog", "/sysuser")
.hasAnyRole("admin") //admin角色可以访问
.antMatchers("/syslog").hasAuthority("/syslog")
.antMatchers("/sysuser").hasAuthority("/sysuser")
.anyRequest().authenticated()*/
.anyRequest().access("@rabcService.hasPermission(request,authentication)")
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
/**
* 用户配置
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
/* auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("123456"))
.roles("user")
.and()
.withUser("admin")
.password(passwordEncoder().encode("123456"))
//.authorities("sys:log","sys:user")
.roles("admin")*/
//通过加载myUserDetailsService可以动态的获取什么角色拥有什么权限
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(passwordEncoder());//配置BCrypt加密
}
/**
* 密码编码器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 静态资源访问
* @param web
*/
@Override
public void configure(WebSecurity web) {
//将项目中静态资源路径开放出来
web.ignoring().antMatchers( "/css/**", "/fonts/**", "/img/**", "/js/**");
}
}