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());
}

到这里我们的登陆就已经顺利完成了,接下来可以登陆看看效果。由于时间不早了,我这里就不做展示了。

posted @ 2020-03-17 18:08  喜欢你的眉眼微笑i  阅读(2274)  评论(1编辑  收藏  举报