一、需要的组件支持:
新版本这里的组件有些问题:
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");