Security改造登陸頁
前面第一章我們做到了Spring Boot集成Security並成功運行,但是,出現了一個問題:
上圖看到,Security內置的登陸頁面實在過於簡單,接下來我們就來改造登陸頁面。
第一章中有解釋過各個模塊之間存放的相關文件,接下來的Java文件及配置將不再講述放在哪個模塊下,還不太清楚的小伙伴可以點擊此處查看我的代碼結構
application配置文件
spring:
security:
# 登陸路徑
login-url: /login
# 登出路徑
logout-url: /logout
# 免認證靜態資源路徑
anon-resources-url: /css/**,/js/**,/skin/**,/images/**,/font/**,/fonts/**,/dist/**
# 放行路徑
release-url: /login
# 記住我超時時間
remember-me-timeout: 300
# 對應登錄頁面 form表單的 action屬性
login-processing-url: /authentication/form
配置屬性
/**
* @Package: com.zlx.bpms.properties
* @Author: LQW
* @Date: 2020/3/17
* @Description:權限認證屬性
*/
@ConfigurationProperties(prefix = "spring.security")
@Data
public class BpmsSecurityProperties {
/**
* 登錄路徑
*/
private String loginUrl;
/**
* 登出路徑
*/
private String logoutUrl;
/**
* 免認證靜態資源路徑
*/
private String anonResourcesUrl;
/**
* 放行路徑
*/
private String releaseUrl;
/**
* 記住我超時時間
*/
private int rememberMeTimeout;
/**
* 處理登陸認證URL(頁面的action屬性值)
*/
private String loginProcessingUrl;
}
Security配置
/**
* @Package: com.zlx.bpms.config
* @Author: LQW
* @Date: 2020/3/17
* @Description:bpms安全配置
*/
@Configuration
@Order(-1) //值越小,優先級越高
@EnableWebSecurity
public class BpmsSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private BpmsSecurityProperties properties;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 放行路徑
String[] releaseUrl = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getReleaseUrl(), ",");
//免認證靜態資源路徑
String[] anonResources = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getAnonResourcesUrl(), ",");
http
//開啟http基本配置
.httpBasic()
.and()
//開啟表單登陸方式
.formLogin()
//登陸的Url地址
.loginPage(properties.getLoginUrl())
//處理登陸認證URL(頁面的action屬性值)
.loginProcessingUrl(properties.getLoginProcessingUrl())
//放行登陸頁面
.permitAll()
.and()
//開啟授權配置
.authorizeRequests()
//放行路徑
.antMatchers(releaseUrl).permitAll()
//免認證靜態資源路徑
.antMatchers(anonResources).permitAll()
//所有請求
.anyRequest()
//都需要認證
.authenticated();
}
控制器(controller)
/**
* @Package: com.zlx.bpms.controller
* @Author: LQW
* @Date: 2020/3/17
* @Description:系統登陸控制器
*/
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(){
return "login";
}
}
html頁面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link th:href="@{css/shop.css}" type="text/css" rel="stylesheet"/>
<link th:href="@{skin/default/skin.css}" rel="stylesheet" type="text/css" id="skin"/>
<link th:href="@{css/Sellerber.css}" type="text/css" rel="stylesheet"/>
<link th:href="@{css/bkg_ui.css}" type="text/css" rel="stylesheet"/>
<link th:href="@{font/css/font-awesome.min.css}" rel="stylesheet" type="text/css"/>
<script data-th-src="@{js/jquery-1.9.1.min.js}" type="text/javascript"></script>
<script data-th-src="@{js/layer/layer.js}" type="text/javascript"></script>
<script data-th-src="@{js/bootstrap.min.js}" type="text/javascript"></script>
<script data-th-src="@{js/Sellerber.js}" type="text/javascript"></script>
<script data-th-src="@{js/shopFrame.js}" type="text/javascript"></script>
<script type="text/javascript" data-th-src="@{js/jquery.cookie.js}"></script>
<title>用戶登錄</title>
</head>
<body class="login-layout Reg_log_style">
<div class="logintop">
<span>歡迎后台管理界面平台</span>
<ul>
<li><a href="#">返回首頁</a></li>
<li><a href="#">幫助</a></li>
<li><a href="#">關於</a></li>
</ul>
</div>
<div class="loginbody">
<div class="login-container">
<div class="center"><img th:src="@{images/logo.png}"/></div>
<div class="space-6"></div>
<div class="position-relative">
<div id="login-box" class="login-box widget-box no-border visible">
<div class="login-main">
<div class="clearfix">
<div class="login_icon"><img th:src="@{images/login_img.png}"/></div>
<form th:action="@{/authentication/form}" method="post"
style=" width:300px; float:right; margin-right:50px;">
<h4 class="title_name"><img th:src="@{images/login_title.png}"/></h4>
<fieldset>
<ul>
<li class="frame_style form_error"><label class="user_icon"></label><input
name="username"
type="text"
data-name="用戶名"
id="username"/><i>用戶名</i>
</li>
<li class="frame_style form_error"><label class="password_icon"></label><input
name="password" type="password" data-name="密碼" id="userpwd"/><i>密碼</i></li>
<li class="frame_style form_error"><label class="Codes_icon"></label><input
name="imageCode"
type="text"
data-name="驗證碼"
id="Codes_text"/><i>驗證碼</i>
<div class="Codes_region"><img th:src="@{images/yanzhengma.png}" width="100%"
height="38px"></div>
</li>
</ul>
<div class="space"></div>
<div class="clearfix">
<label class="inline">
<input type="checkbox" class="ace">
<span class="lbl">保存密碼</span>
</label>
<button type="submit" class="login_btn" id="login_btn"> 登 陸</button>
</div>
<div class="space-4"></div>
</fieldset>
</form>
</div>
<div class="social-or-login center">
<span class="bigger-110">通知</span>
</div>
<div class="social-login ">
為了更好的體驗性,本網站系統不再對IE8(含IE8)以下瀏覽器支持,請見諒。
</div>
</div><!-- /login-main -->
<!-- /widget-body -->
</div><!-- /login-box -->
</div><!-- /position-relative -->
</div>
</div>
<div class="loginbm">版權所有 2016 <a href=""></a></div>
<strong></strong>
</body>
</html>
<script type="text/javascript">
$('#login_btn').on('click', function () {
var num = 0;
var str = "";
$("input[type$='text'],input[type$='password']").each(function (n) {
if ($(this).val() == "") {
layer.alert(str += "" + $(this).attr("data-name") + "不能為空!", {
title: '提示框',
icon: 0,
});
num++;
return false;
}
});
if (num > 0) {
return false;
} else {
layer.alert('登陸成功!', {
title: '提示框',
icon: 1,
});
location.href = "shops_index.html";
layer.close(index);
}
});
$(document).ready(function () {
$("input[type='text'],input[type='password']").blur(function () {
var $el = $(this);
var $parent = $el.parent();
$parent.attr('class', 'frame_style').removeClass(' form_error');
if ($el.val() == '') {
$parent.attr('class', 'frame_style').addClass(' form_error');
}
});
$("input[type='text'],input[type='password']").focus(function () {
var $el = $(this);
var $parent = $el.parent();
$parent.attr('class', 'frame_style').removeClass(' form_errors');
if ($el.val() == '') {
$parent.attr('class', 'frame_style').addClass(' form_errors');
} else {
$parent.attr('class', 'frame_style').removeClass(' form_errors');
}
});
})
</script>
以上都完成后,我們的登陸頁面就已經有了
是不是比之前的好看了許多,不要以為到這里就完了,我們只是把樓房的外表弄出來了,可里面還是空殼子呢!不信點點登陸按鈕就知道了。
數據庫實現登陸
到這里,我們點擊了頁面的登陸按鈕發現並沒有任何反應(但瀏覽器地址欄卻多了一串</font color=#dd0000>?error),那是因為我們還沒有實現登陸者身份驗證。
UserDetailService
/**
* @Package: com.zlx.bpms
* @Author: LQW
* @Date: 2020/3/17
* @Description:用戶詳細信息服務接口實現類
*/
@Component("userDetailService")
public class UserDetailServiceImpl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(UserDetailServiceImpl.class);
@Resource
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.getUserByUserName(username);
//查詢該用戶具有的權限
List<String> list = userService.findRolePermission(username);
log.info("通過用戶名為:{},獲取到的用戶信息為:{}", username, user);
//region 實體類封裝
UserDetail detail = new UserDetail();
if (null != user) {
detail.setUsername(user.getUserName());
detail.setPassword(user.getPassword());
//region 權限組裝
Set<SimpleGrantedAuthority> authoritiesSet = new HashSet<SimpleGrantedAuthority>();
for (String role : list) {
SimpleGrantedAuthority roleAdmin = new SimpleGrantedAuthority(role);
authoritiesSet.add(roleAdmin);
}
//endregion
detail.setAuthorities(authoritiesSet);
}
//endregion
return detail;
}
}
用戶信息
- User
/**
* @Package: com.zlx.bpms.bean
* @Author: LQW
* @Date: 2020/3/17
* @Description:權限認證用戶實體
*/
@TableName("sys_user")
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("remark_name")
private String remarkName;
@TableField("user_name")
private String userName;
@TableField("password")
private String password;
@TableField("phone_number")
private String phoneNumber;
@TableField("create_time")
private Date createTime;
@TableField("update_time")
private Date updateTime;
@TableField("is_status")
private Integer isStatus;
@Override
public String toString() {
return "User{" +
"id=" + id +
", remarkName='" + remarkName + '\'' +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", createTime=" + createTime +
", updateTime=" + updateTime +
", isStatus=" + isStatus +
'}';
}
@Override
protected Serializable pkVal() {
return this.id;
}
}
- UserDetail
/**
* @Package: com.zlx.bpms.bean
* @Author: LQW
* @Date: 2020/3/17
* @Description:用戶詳細信息
*/
public class UserDetail implements UserDetails, Serializable {
private String username;
private String password;
private Set<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
public void setAuthorities(Set<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public String getPassword() { // 最重點Ⅰ
return this.password;
}
@Override
public String getUsername() { // 最重點Ⅱ
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
接口及實現類
- UserService
/**
* @Package: com.zlx.bpms.service
* @Author: LQW
* @Date: 2020/3/17
* @Description:權限認證用戶接口服務
*/
public interface UserService {
/**
* 獲取用戶信息 by 用戶名
*
* @param username 用戶名
* @return User
*/
User getUserByUserName(String username);
/**
* 查找角色權限 by 用戶名
*
* @param username 用戶名
* @return list
*/
List<String> findRolePermission(String username);
}
- UserServiceImpl
/**
* @Package: com.zlx.bpms
* @Author: LQW
* @Date: 2020/3/17
* @Description:認證權限用戶接口實現類
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public User getUserByUserName(String username) {
QueryWrapper<User> ew = new QueryWrapper<>();
ew.eq("user_name", username);
ew.eq("is_status", 1);
return userDao.selectOne(ew);
}
@Override
public List<String> findRolePermission(String username) {
return userDao.findRolePermission(username);
}
}
- UserDao
/**
* @Package: com.zlx.bpms.dao
* @Author: LQW
* @Date: 2020/3/17
* @Description:用戶數據交互接口
*/
public interface UserDao extends BaseMapper<User> {
List<String> findRolePermission(@Param("username") String username);
}
- UserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zlx.bpms.dao.UserDao">
<!-- 通用查詢映射結果 -->
<resultMap id="BaseResultMap" type="com.zlx.bpms.bean.User">
<id column="id" property="id"/>
<result column="remark_name" property="remarkName"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
<result column="phone_number" property="phoneNumber"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="is_status" property="isStatus"/>
</resultMap>
<sql id="overall_column">
id,remark_name,user_name,password,phone_number,create_time,update_time,is_status
</sql>
<select id="findRolePermission" resultType="java.lang.String">
SELECT
ap.role_permission
FROM
sys_permission AS ap
LEFT JOIN sys_user AS au ON ap.user_id = au.id
LEFT JOIN sys_role AS ar ON ar.permission_id = ap.id
AND ar.user_id = au.id
WHERE au.user_name = #{username,jdbcType=VARCHAR}
</select>
</mapper>
數據庫及表設計在我GitHub項目中
修改Security配置
在BpmsSecurityConfig中,添加如下代碼
@Resource
private UserDetailServiceImpl detailService;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//設置密碼編碼器
provider.setPasswordEncoder(passwordEncoder());
//設置 用戶詳細信息服務接口
provider.setUserDetailsService((UserDetailsService) detailService);
// 關閉 隱藏未找到用戶異常
provider.setHideUserNotFoundExceptions(false);
return provider;
}
/**
* 身份驗證管理器
*
* @param auth 身份驗證管理器生成器
* @throws Exception 異常信息
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//目的是為了前端獲取數據時獲取到整個form-data的數據,提供驗證器
auth.authenticationProvider(authenticationProvider());
//配置登錄user驗證處理器 以及密碼加密器 好讓認證中心進行驗證
auth.userDetailsService((UserDetailsService) detailService).passwordEncoder(passwordEncoder());
}
到這里我們的登陸就已經順利完成了,接下來可以登陸看看效果。由於時間不早了,我這里就不做展示了。