10-4-用户登录控制

一、请求处理层实现

创建一个用户登录管理的控制类文件LoginController,并编写向自定义登录页面跳转的请求控制方法

@Controller
public class LoginController {

    // 向登录页面跳转,同时封装原始页面地址
    @GetMapping(value = "/login")
    public String login(HttpServletRequest request, Map map) {

        // 分别获取请求头和参数url中的原始访问路径
        String referer = request.getHeader("Referer");
        //url最初的值从referer中来
        String url = request.getParameter("url");
        System.out.println("referer= "+referer);
        System.out.println("url= "+url);

        // 如果参数url中已经封装了原始页面路径,直接返回该路径
        if (url!=null && !url.equals("")){

            map.put("url",url);
            // 如果请求头本身包含登录,将重定向url设为空,让后台通过用户角色进行选择跳转
        }else if (referer!=null && referer.contains("/login")){
            map.put("url", "");
        }else {

            // 否则的话,就记住请求头中的原始访问路径
            map.put("url", referer);
        }
        return "comm/login";
}

    // 对Security拦截的无权限访问异常处理路径映射
    @GetMapping(value = "/errorPage/{page}/{code}")
    public String AccessExecptionHandler(@PathVariable("page") String page, @PathVariable("code") String code) {
        return page+"/"+code;
    }
}

二、实现前端页面功能

comm文件夹下的自定义用户登录页面login.html进行自定义用户登录功能查看和实现

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>登录博客后台</title>

    <meta http-equiv="Cache-Control" content="no-siteapp"/>
    <link rel="shortcut icon" th:href="@{/user/img/bloglogo.jpg}"/>

    <script th:src="@{/assets/js/jquery.min.js}"></script>
    <script th:src="@{/assets/js/amazeui.min.js}"></script>
    <link rel="stylesheet" th:href="@{/assets/css/amazeui.min.css}"/>
    <link rel="stylesheet" th:href="@{/assets/css/app.css}"/>
</head>

<body>
<div class="log">
    <div class="am-g">
        <div class="am-u-lg-3 am-u-md-6 am-u-sm-8 am-u-sm-centered log-content">
            <h1 class="log-title am-animation-slide-top" style="color: black;" th:text="#{login.welcomeTitle}">~欢迎登录博客~</h1>
            <br>
            <div th:if="${param.error}" style="color: red" th:text="#{login.error}">用户名或密码错误!</div>
            <form class="am-form" id="loginForm" th:action="@{/login}" method="post">
                <div>
                    <input type="hidden" name="url" th:value="${url}">
                </div>
                <div class="am-input-group am-radius am-animation-slide-left">
                    <input type="text" class="am-radius" th:placeholder="#{login.username}" name="username" />
                    <span class="am-input-group-label log-icon am-radius">
                        <i class="am-icon-user am-icon-sm am-icon-fw"></i>
                    </span>
                </div>
                <br>
                <div class="am-input-group am-animation-slide-left log-animation-delay">
                    <input type="password" class="am-form-field am-radius log-input" th:placeholder="#{login.password}" name="password" />
                    <span class="am-input-group-label log-icon am-radius">
                        <i class="am-icon-lock am-icon-sm am-icon-fw"></i>
                    </span>
                </div>
                <div style="padding-top: 10px;">
                    <input type="submit" th:value="#{login.sub}"
                           class="am-btn am-btn-primary am-btn-block am-btn-lg am-radius am-animation-slide-bottom log-animation-delay" />
                </div>
            </form>
        </div>
    </div>

    <footer class="log-footer">
        <p style="margin: 30px; color: #2E2D3C"><time class="comment-time" th:text="${#dates.format(new java.util.Date().getTime(), 'yyyy')}"></time> &copy; Powered By <a style="color: #0e90d2" rel="nofollow">CrazyStone</a></p>
    </footer>
</div>
</body>
</html>

注:

核心内容是编写了一个用户登录的<form>表单,并使用Thymeleaf模板的“#{}”表达式进行了登录表单信息的国际化设置。

该用户登录功能也是使用了<form>表单进行POST方式提交请求的,由于使用了Thymeleaf的th:action="@{/login}"属性进行请求处理,所以无需手动添加携带CSRF Token的隐藏域,表明该请求不是跨站伪造请求。

三、编写Security认证授权配置类

创建一个用于整合Security进行安全控制的配置类SecurityConfig,并重写自定义用户认证和授权方法,。

使用JDBC身份认证的方式实现了自定义用户认证,此时重启项目进行访问,则只需要输入数据库中已有的用户信息就可以登录认证。

自定义用户授权管理方法中主要配置了自定义的用户访问控制、用户登录控制、用户登录后的Cookie设置、用户退出控制和登录用户无权限访问控制。

@EnableWebSecurity  // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Value("${COOKIE.VALIDITY}")
    private Integer COOKIE_VALIDITY;

    /**
     * 重写configure(HttpSecurity http)方法,进行用户授权管理
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 1、自定义用户访问控制
        http.authorizeRequests()
                .antMatchers("/","/page/**","/article/**","/login").permitAll()
                .antMatchers("/back/**","/assets/**","/user/**","/article_img/**").permitAll()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated();
        // 2、自定义用户登录控制
        http.formLogin()
                .loginPage("/login")
                .usernameParameter("username").passwordParameter("password")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        String url = httpServletRequest.getParameter("url");
                        // 获取被拦截的原始访问路径
                        RequestCache requestCache = new HttpSessionRequestCache();
                        SavedRequest savedRequest = requestCache.getRequest(httpServletRequest,httpServletResponse);
                        if(savedRequest !=null){
                            // 如果存在原始拦截路径,登录成功后重定向到原始访问路径
                            httpServletResponse.sendRedirect(savedRequest.getRedirectUrl());
                        } else if(url != null && !url.equals("")){
                            // 跳转到之前所在页面
                            URL fullURL = new URL(url);
                            httpServletResponse.sendRedirect(fullURL.getPath());
                        }else {
                            // 直接登录的用户,根据用户角色分别重定向到后台首页和前台首页
                            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                            boolean isAdmin = authorities.contains(new SimpleGrantedAuthority("ROLE_admin"));
                            if(isAdmin){
                                httpServletResponse.sendRedirect("/admin");
                            }else {
                                httpServletResponse.sendRedirect("/");
                            }
                        }
                    }
                })
                // 用户登录失败处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        // 登录失败后,从提交的隐藏域取出原始页面url并追加在重定向路径上
                        String url = httpServletRequest.getParameter("url");
                        httpServletResponse.sendRedirect("/login?error&url="+url);
                    }
                });
        // 3、设置用户登录后cookie有效期,默认值
        http.rememberMe().alwaysRemember(true).tokenValiditySeconds(COOKIE_VALIDITY);
        // 4、自定义用户退出控制
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
        // 5、针对访问无权限页面出现的403页面进行定制处理
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                // 如果是权限访问异常,则进行拦截到指定错误页面
                RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/errorPage/comm/error_403");
                dispatcher.forward(httpServletRequest, httpServletResponse);
            }
        });
    }

    /**
     * 重写configure(AuthenticationManagerBuilder auth)方法,进行自定义用户认证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //  密码需要设置编码器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        //  使用JDBC进行身份认证
        String userSQL ="select username,password,valid from t_user where username = ?";
        String authoritySQL ="select u.username,a.authority from t_user u,t_authority a," +
                             "t_user_authority ua where ua.user_id=u.id " +
                             "and ua.authority_id=a.id and u.username =?";
        auth.jdbcAuthentication().passwordEncoder(encoder)
                .dataSource(dataSource)
                .usersByUsernameQuery(userSQL)
                .authoritiesByUsernameQuery(authoritySQL);
    }
}

四、效果展示

启动项目进行测试,访问项目前端首页,无需登录,如图。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值