window.cnblogsConfig = { blogUser: 'MoYu', blogAvatar: 'https://gitee.com/MoYu-zc/picgo/raw/master/img/20210213094450.jpg', blogStartDate: '2020-02-09', webpageTitleOnblur: '(o゚v゚)ノ Hi,Back', webpageTitleOnblurTimeOut: 500, webpageTitleFocus: '(*´∇`*) 欢迎回来!', webpageTitleFocusTimeOut: 1000, webpageIcon: "https://gitee.com/MoYu-zc/picgo/raw/master/img/20210213094450.jpg", enable: true, // 是否开启日/夜间模式切换按钮 auto: { // 自动切换相关配置 enable: false, // 开启自动切换 dayHour: 7, // 日间模式开始时间,整数型,24小时制 nightHour: 20 // 夜间模式开始时间,整数型,24小时制 } switchDayNight: { enable: true, auto: { enable: true } }, progressBar: { id : 'top-progress-bar', // 请勿修改该值 color : '#77b6ff', height : '2px', duration: 0.2, }, loading: { rebound: { tension: 16, friction: 5, }, spinner: { id: 'spinner', radius: 90, sides: 3, depth: 4, colors: { background: '#f0f0f0', stroke: '#272633', base: null, child: '#272633', }, alwaysForward: true, // When false the spring will reverse normally. restAt: 0.5, // A number from 0.1 to 0.9 || null for full rotation renderBase: false, } }, homeTopAnimationRendered: true, homeTopAnimation: { radius: 15, density: 0.2, color: 'rgba(255,255,255, .2)', // 颜色设置,“random” 为随机颜色 clearOffset: 0.3, }, essayTopAnimationRendered: true, essayTopAnimation: { triW : 14, triH : 20, neighbours : ["side", "top", "bottom"], speedTrailAppear : .1, speedTrailDisappear : .1, speedTriOpen : 1, trailMaxLength : 30, trailIntervalCreation : 100, delayBeforeDisappear : 2, colorsRandom: false, // v1.2.4 是否开启随机颜色 colors: [ '#96EDA6', '#5BC6A9', '#38668C', '#374D84', '#BED5CB', '#62ADC6', '#8EE5DE', '#304E7B' ] }, homeTopImg: [ "https://cdn.jsdelivr.net/gh/BNDong/Cnblogs-Theme-SimpleMemory@master/img/webp/home_top_bg.webp", "https://cdn.jsdelivr.net/gh/BNDong/Cnblogs-Theme-SimpleMemory@master/img/webp/home_top_bg.webp" ], homeBannerTextType: "one", essayTopImg: [ "https://cdn.jsdelivr.net/gh/BNDong/Cnblogs-Theme-SimpleMemory@master/img/webp/nothome_top_bg.webp", "https://cdn.jsdelivr.net/gh/BNDong/Cnblogs-Theme-SimpleMemory@master/img/webp/nothome_top_bg.webp", "https://gitee.com/MoYu-zc/picgo/raw/master/img/20210208190902.jpg", "https://gitee.com/MoYu-zc/picgo/raw/master/img/20210208190954.jpg", ], codeMaxHeight: true, codeLineNumber: true, essayCode: { fontFamily: "'Ubuntu Mono',monospace", // 代码框字体 fontSize: "14px" // 代码框字体大小 }, }

Spring Security 下

Spring Security 下

Security 注解使用

1.@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀 ROLE

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(securedEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@Secured({"ROLE_sale","ROLE_man"})   //有该权限才可以
public String update(){
    return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

你登陆之后,如果登录用户没有对应权限,就算是可以登陆上去,也无法进入该路径

2.@PreAuthorize

该注解适合进入方法前的权限验证 ,可以将登录用户的 roles/permissions 参数传到方法中。.

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@PreAuthorize("hasAnyAuthority('admins')")
    /* @PreAuthorize("hasAnyAuthority('admins')") 
    *  @PreAuthorize("hasRole('admins')")
    */ @PreAuthorize("hasAnyRole('admins')")
public String update(){
    return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

该方法,与在配置类中给网页加上进入权限相同,连四个方法都完全一样

3.@PostAuthorize

该注解使用不多,在方法执行后再进行权限验证,适合验证带有返回值的权限

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@PostAuthorize("hasAnyAuthority('admin')")  //也是参数为 四种方法
public String update(){
    System.out.println("update。。。");
    return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

该注解下,虽然权限不足仍然无法进入页面或者返回信息,但是可以先运行方法体中的内容,再校验

所以对于上面的实例,进入路径后,虽然还是显示 没权限403,但是 update 会输出

4.@PostFilter

权限验证之后对返回的数据进行过滤

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("getAll")
@PostAuthorize("hasAnyAuthority('admins') ")
@PostFilter("filterObject.username == 'admin1'")
public List<Users> getAllUser() {
    ArrayList<Users> list = new ArrayList<>();
    list.add(new Users(1, "admin1", "666"));
    list.add(new Users(2, " admin2", "888"));
    return list;
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

1

5.@PreFilter

进入控制器之前对传递回来的数据进行过滤

@PostFilter 类似, 不做演示

用户注销

1.创建登录成功页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    登录成功!
    <a href="/logout">退出</a>
</body>
</html>

2.配置类中配置注销功能

@Override
protected void configure(HttpSecurity http) throws Exception {
    //注销,第一个路径为 退出是设置的路径 ; 第二个路径为 成功退出后的路径
    http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll();
    http.exceptionHandling().accessDeniedPage("/uuauth.html");
    http.formLogin()   
        .loginPage("/login.html") 
        .loginProcessingUrl("/user/login") 
        .defaultSuccessUrl("/success.html").permitAll()  //这里更换为登录成功页面
        .and().authorizeRequests()
        .antMatchers("/","/hello","/user/login") 
        .permitAll()
        .antMatchers("/index").hasRole("user")
        .anyRequest().authenticated()
        .and().csrf().disable(); 
}

3.运行测试

上述两个操作已经足够了

登录页面之后,会出现在 成功登录页面 ,这时候 你进入其他的页面也是可以的(如果权限允许)

点击退出,返回设置好的页面,这时候 其他页面都无法进入

基于数据库实现记住我

2

步骤:

  1. 创建数据库

  2. 配置类 、配置数据源

  3. 配置类中 配置 自动登录

  4. 登陆页面添加 复选框


1.创建数据库

该数据库语句是 Security 框架中 内置的创建语句

CREATE TABLE `persistent_logins`( 
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL, 
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (series)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.配置数据源

@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    // jdbcTokenRepository.setCreateTableOnStartup(true);  启动时 创建表
    return jdbcTokenRepository;
}

3.配置自动登录

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll();
    http.exceptionHandling().accessDeniedPage("/uuauth.html");
    http.formLogin()   
        .loginPage("/login.html")  
        .loginProcessingUrl("/user/login") 
        .defaultSuccessUrl("/success.html").permitAll()  
        .and().authorizeRequests()
        .antMatchers("/","/hello","/user/login") 
        .permitAll()   
        .antMatchers("/index").hasRole("user")
        .anyRequest().authenticated()
        
        .and().rememberMe().tokenRepository(persistentTokenRepository())
        .tokenValiditySeconds(60)   //设置有效时长
        .userDetailsService(userDetailsService)

        .and().csrf().disable(); 
}

4.登陆页面添加 记住我

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="/user/login">
        用户名:<input type="text" name="username">
        <br>
        密码:<input type="text" name="password">
        <br>
        <input type="checkbox" name="remember-me"> 自动登录   <!-- name 必须为 "remember-me"  -->
        <br>
        <input type="submit" value="login">
    </form>
</body>
</html>

5.运行测试

等着之后,数据会存在 cookie 中 ; 也因为数据库为框架内部设置好的,所以也会存在数据库表中

3

4

CSRF 保护

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack“或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意攻击。是一种依赖web浏览器的、被混淆过的代理人攻击。

常见特性:

  • 依靠用户标识危害网站
  • 利用网站对用户表识的信任
  • 欺骗用户的浏览器发送HTTP请求给目标站点
  • 可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。

该种方式只能对 POSTPUTDELETEPATCH 发送方式进行保护

1.引入依赖

<!-- thymeleaf 引擎 -->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    <version>3.0.2.RELEASE</version>
</dependency>

2.创建CSRF测试前端页面

先直接在static文件夹下创建一个login页面

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="/user/login">
        用户名:<input type="text" name="username">
        <br>
        密码:<input type="text" name="password">
        <br>
        <input type="checkbox" name="remember-me"> 自动登录
        <br>
         <input type="submit" value="login">
    </form>
</body>
</html>

首先在resources文件夹下创建 templates 文件夹, Spring Boot 默认Controller层跳转路径为该文件夹下页面

csrf.html

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="token">
        <input type="hidden"  th:name= "${_csrf.parameterName}" th:value="${_csrf.token}" />
        用户名:<input type="text" name="username">
        <br>
        密码:<input type="text" name="password">

         <input type="submit" value="login">
    </form>
</body>
</html>

csrf_success.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <span th:text="${_csrf.token}"></span> <br>
</body>
</html>

3.创建Controller层代码

@Controller
public class CSRFController {

    @GetMapping("/csrf")
    public String csrf(Model model) {
        return "csrf";
    }
    @PostMapping("/csrf_success")
    public String success() {
        return "csrf_success";
    }
}

4.修改配置类代码

http.formLogin()
    .loginProcessingUrl("/user/login") 
    .defaultSuccessUrl("/").permitAll() 
    .and().authorizeRequests()
    .antMatchers("/","/csrf","/user/login") 
    .permitAll();
//	.and().csrf().disable(); //关闭csrf

5.运行测试

这时候已经将CSRF开启了,这时候进入测试路径:

http://localhost:8081/csrf

输入账号密码,会成功进入 /csrf_success ; 并且页面中显示 _csrf.token

5

个人博客为:
MoYu's Github Blog
MoYu's Gitee Blog

posted @ 2021-05-06 20:31  MoYu-zc  阅读(86)  评论(0编辑  收藏  举报