Spring Boot 2 + Spring Security 5 自定义登录界面

本文介绍如何在Spring Boot项目中集成Spring Security进行用户登录认证,包括配置依赖、自定义SecurityConfig、实现登录控制器及页面展示。

1、在pom.xml下<dependencies>添加项目依赖:


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    


2、创建SecurityConfig类并继承WebSecurityConfigurerAdapter类,用于Spring Security 5的配置:

 

package com.example.testssm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.*;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 拦截请求
     * 配置Spring Security的Filter链
     * @param web
     * @throws Exception
     */

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

    /**
     * 拦截请求
     * 配置如何通过拦截器保护请求
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login", "/form", "/register", "/register_save", "/regusernameajax", "/druid/**", "/error", "/verifycode")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/form")
                .permitAll()
                .usernameParameter("username")
                .passwordParameter("password")
                //.defaultSuccessUrl("/index")
                //.successForwardUrl("/index")
                //.failureForwardUrl("/error")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .deleteCookies()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .permitAll()
                .and()
                .csrf().disable();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

 

3、创建LoginController控制器类,用于用户登录处理:

 

package com.example.testssm.controller;

import com.example.testssm.dao.LoginUser;
import com.example.testssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class LoginController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
    public String login(Model model){
        return "login";
    }

    @RequestMapping(value = {"/", "/form"}, method = {RequestMethod.GET, RequestMethod.POST})
    public String form() {

        return "redirect:/index";
    }

    @GetMapping("/register")
    public String register(){
        return "register";
    }


    @PostMapping("/regusernameajax")
    @ResponseBody
    public String is_exist_name(String username) {

        LoginUser loginUser = userService.getUserByName(username);

        if(loginUser == null) {
            return "false";
        }

        return "true";
    }

    @PostMapping("/register_save")
    public String registerSave(String username, String password2){
        int ret = 0;

        if(username.equals("admin")){
            ret = userService.insertUserBasic("admin", password2, "admin");
        }else{
            ret = userService.insertUserBasic(username, password2, "user");
        }

        if(ret <= 0){
            return "register_error";
        } else {
            return "redirect:/login";
        }
    }
}


4、创建MainController控制器类,用于登录成功后的页面显示:


package com.example.testssm.controller;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;


@Controller
public class MainController {

    @RequestMapping(value="/index", method = RequestMethod.GET)
    public String index(Model model){

        String userName = new String();

        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (principal instanceof UserDetails) {
            userName = ((UserDetails) principal).getUsername();
            Collection<? extends GrantedAuthority> authorities = ((UserDetails) principal).getAuthorities();
            for (GrantedAuthority grantedAuthority : authorities) {
                System.out.println("当前用户 " + userName + " 权限 " + grantedAuthority.getAuthority());
            }
        } else {
            userName = "无法获取用户";
        }

        model.addAttribute("username", userName);

        return "index";
    }
}

 

5、创建login.html登录页面,基于thymeleaf模板:

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" type="text/css" href="./css/login.css">
    <link rel="stylesheet" type="text/css" href="./css/Font-Awesome-3.2.1/css/font-awesome.min.css" />
    <span th:replace="head_js :: head_jquery"></span>
</head>
<body>

<div id="loginbox">
    <div id="loginboxhead">&nbsp;&nbsp;<span class="icon-user icon-large"></span></div>
    <br />
    <div class="content">
        <span th:if="${param.error}" class="span1">
            Invalid username and password.
        </span>
        <span th:if="${param.logout}" class="span1">
            You have been logged out.
        </span>
        <div id="login_user" style="padding-left: 50px; display: none;"></div>

        <form id="formlogin" th:action="@{/form}" method="post" onSubmit="return confirmInput();">
            <table>
                <tr>
                    <td class="td1">用户名:</td>
                    <td class="td2"><input id="username" name="username" type="text" size="30" /></td>
                </tr>
                <tr>
                    <td class="td1">密&nbsp;&nbsp;码:</td>
                    <td class="td2"><input id="password" name="password" type="password" size="30" /></td>
                </tr>
            </table>
            <table>
                <tr>
                    <td class="td1"><span class="prompt">点击刷新:</span></td>
                    <td class="td2"><a href="javascript:void(0);" onclick="changecode();">
                            <img th:src="@{/verifycode}" id="codeImg" width="100px" height="50px" style="border:1px solid #cccccc; vertical-align:middle;" alt="验证码图片,点我换新图" />
                        </a>
                    </td>
                </tr>
                <tr>
                    <td class="td1">验证码:</td>
                    <td class="td2"><input id="checkcode" type="text" name="checkcode" disabled="disabled" /></td>
                </tr>
            </table>
            <table>
                <tr>
                    <td class="td3"><input type="submit" value="登  录"/></td>
                    <td class="td4"><input type="reset" value="重  置" /></td>
                    <td class="td5"><a th:href="@{/register}"><button type="button">注 册</button></a></td>
                </tr>
            </table>
        </form>
    </div>
</div>


<script type="text/javascript" language="javascript">
    //生成随机参数
    function getRand() {
        var mydate = new Date();
        var rnd = Math.floor(Math.random() * 9999) + 1;
        var result = mydate.getHours().toString() + mydate.getMinutes().toString() + mydate.getSeconds().toString() + rnd;
        return result;
    }

    function changecode(){ // 更换验证码
        $('#codeImg').attr("src", "[[@{/}]]verifycode"  + "?rnd=" + getRand());
    }

    //字符串为空
    function isNull(str) {
        if (str == null || str == "" || str.length < 1)
            return true;
        else
            return false;
    }

     //在客户端验证用户输入合法性
    function confirmInput()
    {
        if(formlogin.username.value == ''){
            alert('请填写您的用户名!');
            $('#username').focus();
            return false;
        }

        if(formlogin.password.value == ''){
            alert('请填写您的密码!');
            $('#password').focus();
            return false;
        }

        return true;
    }

    window.onload = function(event){

        var event = window.event || event;

        var loginboxObj = document.getElementById('loginbox');

        var posX = document.body.offsetWidth / 2 - loginboxObj.offsetWidth / 2;

        loginboxObj.style.left = posX + 'px';
        loginboxObj.style.top = '200px';

        document.getElementById('loginboxhead').onmousedown = function(event){
            var event = window.event || event;

            var loginboxObj = document.getElementById('loginbox');

            var top = loginboxObj.offsetTop;
            var left = loginboxObj.offsetLeft;

            var elementX = parseInt(event.clientX - left);
            var elementY = parseInt(event.clientY - top);

            document.onmousemove = function(event){
                var event = window.event || event;

                var X = parseInt(event.clientX - elementX);
                var Y = parseInt(event.clientY - elementY);

                var winW = document.documentElement.clientWidth || document.body.clientWidth;
                var winH = document.documentElement.clientHeight || document.body.clientHeight;

                var maxW = winW - loginboxObj.offsetWidth;
                var maxH = winH - loginboxObj.offsetHeight;

                if (X < 0){
                    X = 0;
                } else if(X > maxW){
                    X = maxW;
                }

                if (Y < 0){
                    Y = 0
                } else if(Y > maxH){
                    Y = maxH;
                }

                loginboxObj.style.left = X + 'px';
                loginboxObj.style.top = Y + 'px';
            }
        }

        document.getElementById('loginboxhead').onmouseup = function(event){
            document.onmousemove = null;
        }

        $('#loginboxhead').tooltip({
            position: 'top',
            content: '<span style="color:#fff">拖动可以移动</span>',
            onShow: function(){
                $(this).tooltip('tip').css({
                    backgroundColor: '#666',
                    borderColor: '#666'
                });
            }
        });
    }
</script>

</body>
</html>

 

6、创建index.html页面,基于thymeleaf模板,用于登录成功后的跳转:


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
<p th:text="${username}"></p>
<p>主&nbsp;页</p>
<br />
<a th:href="@{/logout}">退 出</a>
</body>
</html>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝壳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值