Spring Boot Security 實現后台&權限管理系統(二)


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"> 登&nbsp;陸</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());
}

到這里我們的登陸就已經順利完成了,接下來可以登陸看看效果。由於時間不早了,我這里就不做展示了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM