SpringBoot---SpringSecurity(自定义登录页面+Csrf-Token配置)
SpringSecurity
1、SpringSecurity主要还是在配置文件里面配置
- 类前面添加注解 @EnableWebSecurity
- 继承 WebSecurityConfigurerAdapter
- 注入自带的DataSource
- 注入重写的UserDetailsService
- 重写用户身份认证方法 configure(AuthenticationManagerBuilder auth)
- 重写用户授权方法 configure(HttpSecurity http)
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired DataSource dataSource; //自带的
@Resource UserDetailsService userDetailsService; //需要自己重写 //实现用户身份认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); auth.userDetailsService(userDetailsService).passwordEncoder(encoder); } @Override protected void configure(HttpSecurity http) throws Exception { //配置url的访问权限 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/**update**").permitAll() .antMatchers("/login/**").permitAll() .antMatchers("/detail/common/**").hasRole("common") //common权限控制 .antMatchers("/detail/vip/**").hasRole("vip") //vip权限控制 .anyRequest().authenticated(); //关闭csrf保护功能 http.csrf().disable(); //使用自定义的登录窗口 http.formLogin() .loginPage("/userLogin").permitAll() //userLogin对应控制器中的自定义登录路径 .usernameParameter("username").passwordParameter("password") //username和password对应前端表单的name键 .defaultSuccessUrl("/") //登录成功后跳转 .failureUrl("/userLogin?error"); //登录失败跳转
//实现注销 (会清空session) http.logout() .logoutUrl("/mylogout") //只能是post方法,/mylogout是前端的请求 .logoutSuccessUrl("/userLogin"); //记住我 http.rememberMe() .rememberMeParameter("rememberme").tokenValiditySeconds(200) //200秒 .tokenRepository(tokenRepository()); //配置持久化token } //持久化token存储
//数据库的表必须是persistent_logins ,字段必须是username、series(序列号)、token、last_used(更新时间)
@Bean public JdbcTokenRepositoryImpl tokenRepository(){ JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl(); jr.setDataSource(dataSource); return jr; } }
2、登录跳转控制器
@Controller public class LoginController { @GetMapping("/userLogin") public String login(){ return "login/login";}}
3、重写 loadUserByUsername 方法
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired TCustomerService customerService; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //获取用户对象 TCustomer customer = customerService.getByUsername(s); if (customer != null) { List<TAuthority> authorities = customerService.listAuthorityByUsername(s); //对用户权限进行转换 List<SimpleGrantedAuthority> list = authorities.stream() .map(authority -> new SimpleGrantedAuthority(authority.getAuthority())) .collect(Collectors.toList()); // System.out.println("封装userDetails"); UserDetails userDetails = new User(customer.getUsername(), customer.getPassword() , list); return userDetails; } else { System.out.println("用户名不存在"); throw new UsernameNotFoundException("当前用户不存在"); } } }
4、登录页面login.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>用户登录界面</title> <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet"> <link th:href="@{/login/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <form class="form-signin" th:method="post" th:action="@{/userLogin}"> <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72px" height="72px"> <h1 class="h3 mb-3 font-weight-normal">请登录</h1> <!-- 用户登录错误信息提示框 --> <div th:if="${param.error}" style="color:red;height:40px;text-align:left;font-size:1.1em"> <img th:src="@{/login/img/loginError.jpg}" width="20px">用户名或密码错误,请重新登录! </div>
<!--username和password要和哦欸之文件的key一 一对应-->
<input type="text" class="form-control" placeholder="用户名" required="" autofocus="" name="username"> <input type="password" class="form-control" placeholder="密码" required="" name="password"> <div class="checkbox mb-3"> <label> <input type="checkbox" name="rememberme"> 记住我 <!--rememberme要与配置文件的key一 一对应--> </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button> <p class="mt-5 mb-3 text-muted">Copyright© 2019-2020</p> </form> </body> </html>
5、注销前端 (th:action="@{/mylogout}" 的mylogout必须与配置的路径对应,且请求方法为post)
<form th:action="@{/mylogout}" method="post"> <input th:type="submit" th:value="注销" /> </form>
5、常用标签
(1)sec:authorize="isAuthenticated()"
判断用户是否已经登陆认证,引号内的参数必须是isAuthenticated()。
(2)sec:authentication=“name”
获得当前用户的用户名,引号内的参数必须是name。
(3)sec:authorize=“hasRole(‘role’)”
判断当前用户是否拥有指定的权限。引号内的参数为权限的名称。
(4)sec:authentication="principal.authorities"
获得当前用户的全部角色,引号内的参数必须是principal.authorities。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<body> <h1 align="center">欢迎进入电影网站首页</h1> <div sec:authorize="isAnonymous()"> <h2 align="center">游客您好,如果想查看电影<a th:href="@{/userLogin}">请登录</a></h2> </div> <div sec:authorize="isAuthenticated()"> <h2 align="center"><span sec:authentication="name" style="color: #007bff"></span>您好,您的用户权限为 <span sec:authentication="principal.authorities" style="color:darkkhaki"></span>,您有权观看以下电影</h2> <form th:action="@{/mylogout}" method="post"> <input th:type="submit" th:value="注销" /> </form> </div> <hr> <div sec:authorize="hasRole('common')"> <h3>普通电影</h3> <ul> <li><a th:href="@{/detail/common/1}">飞驰人生</a></li> <li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li> </ul> </div> <div sec:authorize="hasAuthority('ROLE_vip')"> <h3>VIP专享</h3> <ul> <li><a th:href="@{/detail/vip/1}">速度与激情</a></li> <li><a th:href="@{/detail/vip/2}">猩球崛起</a></li> </ul> </div> </body>
6、结合security使用csrf-token前端
<form method="post" action="updateUser"> //针对表单数据修改的CSRF Token配置 <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> 用户名: <input type="text" name="username" /><br /> 密 码: <input type="password" name="password" /><br /> <button type="submit">修改</button> </form>
<head> //针对ajax数据修改的CSRF Token配置 <meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_header" th:content="${_csrf.headerName}"/> </head> $(function () { var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $(document).ajaxSend(function(e, xhr, options) { xhr.setRequestHeader(header, token); }); });
后台获取数据
@ResponseBody @PostMapping("/updateUser") public String updateUser(String username,String password,HttpServletRequest request){ System.out.println("username:"+username); System.out.println("password:"+password); String csrf_token=request.getParameter("_csrf"); System.out.println("csrf_token:"+csrf_token); return "OK"; }
csrf讲解:https://blog.csdn.net/xiaoxinshuaiga/article/details/80766369
源码
链接:https://pan.baidu.com/s/1qzmrZCe315l87GeMrAAP5Q
提取码:yuwt


浙公网安备 33010602011771号