【Spring-Security】Re08 Thymeleaf权限控制 与 退出功能

一、需要的组件支持:

新版本这里的组件有些问题:

https://blog.csdn.net/qq_36488647/article/details/104532754
https://blog.csdn.net/YzVermicelli/article/details/106417610

然后我这里就是需要降低下一个版本,Maven依赖就不会爆红了【SpringBoot2.3.4版本】

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

Security本体的组件:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
</dependency>

Web支持 + Thymeleaf模板引擎:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

二、属性获取

然后要使用的模板目录内的页面文件需要导入Thyemleaf + Security的约束

注意一定是使用这个约束地址,新版本的地址反而无效了。。。

<html lang="en"xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

我们可以获取的用户信息:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Main-Page</title>
</head>
<body>
    <h3>This is main page</h3>

    <p>登录账号 <span sec:authentication="name"></span></p>
    <p>登录账号 <span sec:authentication="principal.username"></span></p>
    <p>凭证 <span sec:authentication="credentials"></span></p>
    <p>权限角色集合 <span sec:authentication="authorities"></span></p>
    <p>IP地址 <span sec:authentication="details.remoteAddress"></span></p>
    <p>会话ID <span sec:authentication="details.sessionId"></span></p>
</body>
</html>

访问查看:

三、权限判断:

现在我在用户权限赋予中增加角色和权限:

package cn.zeal4j.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * @author Administrator
 * @file IntelliJ IDEA Spring-Security-Tutorial
 * @create 2020 09 27 21:57
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 1、通过提供的用户名参数访问数据库,查询记录返回过来,如果记录不存在则抛出异常
        // username = "admin";
        if (!"admin".equals(username)) throw new UsernameNotFoundException("用户名不存在");

        // 2、查询出来的凭证是被加密了的,这里是模拟查询的密码
        String encode = passwordEncoder.encode("123456");

        // 权限不可以为空,所以需要这么一个工具方法简单实现
        return new User(username, encode, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_vip-01,ROLE_A,ROLE_B,ROLE_C,/create,/update,/delete"));
    }
}

在页面中的权限控制案例:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Main-Page</title>
    <style type="text/css">
        h3, p {
            text-align: center;
        }
    </style>
</head>
<body>
    <h3>This is main page</h3>

    <p>登录账号 <span sec:authentication="name"></span></p>
    <p>登录账号 <span sec:authentication="principal.username"></span></p>
    <p>凭证 <span sec:authentication="credentials"></span></p>
    <p>权限角色集合 <span sec:authentication="authorities"></span></p>
    <p>IP地址 <span sec:authentication="details.remoteAddress"></span></p>
    <p>会话ID <span sec:authentication="details.sessionId"></span></p>

    <h3>权限控制展示</h3>
    <p> <button sec:authorize="hasRole('A')" >角色A</button> </p>
    <p> <button sec:authorize="hasRole('B')" >角色B</button> </p>
    <p> <button sec:authorize="hasRole('C')" >角色C</button> </p>
    <p> <button sec:authorize="hasRole('D')" >角色D</button> </p>
    
    <p> <button sec:authorize="hasAuthority('/create')" >创建权限</button> </p>
    <p> <button sec:authorize="hasAuthority('/update')" >更新权限</button> </p>
    <p> <button sec:authorize="hasAuthority('/delete')" >删除权限</button> </p>
    <p> <button sec:authorize="hasAuthority('/select')" >查询权限</button> </p>
</body>
</html>

访问查看:

可以看到角色D和SELECT权限都没有,Security对应也不会显示这些按钮

四、退出功能:

Security默认提供了Logout退出控制

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Project-Index</title>
</head>
<body>
    <h1>Hello Spring-Security !!!</h1>
    <a href="/logout">click to logout</a>
</body>
</html>

点击会自动重定向到登录页面来,并且会有一个logout参数值在地址中:

如果不希望附带这个参数,则需要配置退出的处理:

package cn.zeal4j.configuration;

import cn.zeal4j.handler.CustomAccessDeniedHandler;
import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.parameters.P;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author Administrator
 * @file IntelliJ IDEA Spring-Security-Tutorial
 * @create 2020 09 27 21:55
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;
    @Qualifier("userDetailsServiceImpl")
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource); // 数据源注入
        jdbcTokenRepository.setCreateTableOnStartup(false); // 由Security完成Token表的创建,如果有了就设置false关闭
        return jdbcTokenRepository;
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                // 登陆请求参数设置
                usernameParameter("username").
                passwordParameter("password").

                loginPage("/login.html"). // 设置登陆页面URL路径
                loginProcessingUrl("/login.action"). // 设置表单提交URL路径

                successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求

                //  successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                //  failureHandler(new FarsAuthenticationFailureHandler("/error.html")).; // 跨域处理,不需要跳转了

        httpSecurity.authorizeRequests().
                regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式

                antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行

                antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                antMatchers("/admin.page").hasAnyAuthority("admin").

                /*antMatchers("/vip-01.page").hasAnyAuthority("vip-01").*/
                antMatchers("/vip-01.page").hasRole("vip-01").
                antMatchers("/ip.page").hasIpAddress("192.168.43.180").

                // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀

                anyRequest().authenticated(); // 其他请求均需要被授权访问
                // anyRequest().access("@customServiceImpl.hasPermission(request, authentication)"); // 自定义Access配置

        // CSRF攻击拦截关闭
        httpSecurity.csrf().disable();
        httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);


        // 记住我
        httpSecurity.rememberMe().
                tokenValiditySeconds(60). // 设置Token有效时间, 以秒为单位取值
                userDetailsService(userDetailsService).
                tokenRepository(persistentTokenRepository);
        
        // 退出登录处理
        httpSecurity.logout().logoutSuccessUrl("/login.html");
    }
}

如果需要配置独特的退出URL也可以设置:

// 退出登录处理
httpSecurity.
        logout().
        // logoutUrl("/xxx/xxx/logout").
        logoutSuccessUrl("/login.html");

 

posted @ 2020-09-29 10:27  emdzz  阅读(1014)  评论(0编辑  收藏  举报