之前也想过,怎么样最为简单的实现权限的分离和用户的认证呢,学习了一下SpringSecurity,发现它能帮我们完成很多事情,目前来说只知道怎么去用,后面再仔细去研究。
想在SpringBoot中整合这些,先梳理一下思路。提供可以登录注册的2个表单,用户登录后可以进入首页(用户和管理员都能访问)。用户和管理员的权限不同,访问的页面也不同,用户注销后可以访问除首页登录注册页意外的页面会被拦截,自动跳到登录页。
知道大概思路开始设计前端页面了,使用SpringBoot索性就搭配thymeleaf模板了。
登录页:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<head>
<meta charset="UTF-8">
<title>userLogin</title>
</head>
<body>
<h1 align="center">UserLogin</h1>
<div align="center">
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" id="loginform" method="post">
<div><label>用户名:<input name="username" placeholder="请输入用户名"/></label></div>
<div><label>密码:<input name="password" placeholder="请输入密码"/></label></div>
<div><label><input type="text" name="verification" id="verification" class="form-control" placeholder="请输入验证码">
<img id="imgVerify" src="" onclick="getVerify();"
alt="点击更换验证码"/><a href="#" onclick="getVerify();" rel="nofollow">看不清,换一张</a></label></div>
<input type="button" onclick="checkCode()" value="提交"/>
</form>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document.body).ready(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);
});
//首次获取验证码
$("#imgVerify").attr("src", "/getVerify?" + Math.random());
});
//获取验证码
function getVerify() {
var src1 = document.getElementById('imgVerify')
src1.src = "/getVerify?" + Math.random();
}
</script>
<script type="text/javascript">
function checkCode() {
var checkCode = $("#verification").val().toLowerCase();
$.post("/checkCode", {'checkCode': checkCode}, function (result) {
if (result.success == "true") {
$("#loginform").submit();
} else {
alert(result.errorInfo);
}
}, "json");
}
</script>
</body>
</html> 注册页:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>userRegister</title>
</head>
<body>
<h1 align="center">UserRegister</h1>
<div align="center">
<form th:action="@{/register}" method="post">
<table>
<tr>
用户名:<input name="username" placeholder="请输入用户名"/><br>
<div id="udiv" style="color: crimson"></div>
密 码:<input name="password" placeholder="请输入密码"/><br>
<div id="pdiv" style="color: crimson"></div>
手机号:<input name="tel" placeholder="请输入手机号码"/><br>
<div id="tdiv" style="color: crimson"></div>
角色:<select name="role">
<option value="ADMIN,USER">管理员</option>
<option value="USER">用户</option>
</select>
<input type="submit" value="提 交"/>
</tr>
</table>
</form>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
//校验用户名
$("input[name='username']").blur(function () { //失去焦点
var namestr = $(this).val();
var regstr = /^[a-z0-9_-]{3,6}$/;
if (!regstr.test(namestr)) {
$("#udiv").show().text("请输3到6位用户名!");
$("#udiv").fadeOut(3000);
return false;
}
return true;
});
//校验密码
$("input[name='password']").blur(function () { //失去焦点
var namestr = $(this).val();
var regstr = /^[a-z0-9_-]{3,6}$/;
if (!regstr.test(namestr)) {
$("#pdiv").show().text("请输3到6位密码!");
$("#pdiv").fadeOut(3000);
return false;
}
return true;
});
//校验手机号码
$("input[name='tel']").blur(function () { //失去焦点
var telstr = $(this).val();
var regstr = /1\d{10}/;
if (!regstr.test(telstr)) {
$("#tdiv").show().text("请输入正确的11位手机号格式!");
$("#tdiv").fadeOut(3000);
return false;
}
return true;
});
</script>
</body>
</html> SecurityConfig
package cn.seczone.demo.config.security;
import cn.seczone.demo.service.UserService;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
/*http.csrf().disable()*/
http
.authorizeRequests()
.antMatchers("/favicon.ico","/css/**","/common/**","/js/**","/images/**","/login","/register","/login-error","/home/**","/").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/checkCode").permitAll()
.antMatchers("/getVerify").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/home/toLogin")
.loginProcessingUrl("/login")
.successForwardUrl("/home/hello")
.failureForwardUrl("/home/error")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
} UserService实现UserDetailService
package cn.seczone.demo.service;
import cn.seczone.demo.dao.UserDao;
import cn.seczone.demo.model.User;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user = userDao.findUserByUsername(name);
if(user==null){
throw new UsernameNotFoundException("用户名不存在");
}
return user;
}
} User实体要实现UserDetails接口:
/**
* @description: 账户是否过期
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* @description: 账户是否被冻结
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* @description: 账户密码是否过期(对于高密码安全性场景)
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* @description: 账号是否可用
* @return
*/
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
String[] roles = this.getRole().split(",");
for (String role: roles
) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
}
return authorities;
} 这样基本的Security权限管理就搭建好了!