springboot2集成security实现自定义页面登录

security的基本介绍

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,跟前面写的shiro其实是相同功能。核心功能:

  1. 认证(你是谁)
  2. 授权(你能干什么)
  3. 攻击防护(防止伪造身份)

前提说明

首先搭好springboot项目,回头有时间了我会补上github上的demo。需要说明的是这么篇文章只是介绍怎么使用security,不会有原理性的说明。

引入security框架

使用Spring Security很简单,只要在pom.xml文件中,引入spring security的依赖就可以了。

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

什么都不需要配置,启动项目,访问127.0.0.1:80/login就会默认进入security框架提供的登录页面,证明集成security成功。

配置security实现自定义登录

直接上配置代码,注释都写的很清楚,从主配置方法1开始按照注释步骤配置需要项

/**
 * @Author: tanleijin
 * @description (Security配置文件)
 * @Date:2019/9/6 16:01
 */
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启方法权限控制
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SecurityProperties securityProperties;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationSuccessHandler defaultAuthenticationSuccessHandler;
    @Autowired
    private AuthenticationFailureHandler defaultAuthenticationFailureHandler;

    /**
     * 记住我实现
     */
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        jdbcTokenRepositoryImpl.setDataSource(dataSource);
        // 该对象里面有定义创建表的语句
        // 可以设置让该类来创建表(persistent_logins)
        jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);
        return jdbcTokenRepositoryImpl;
    }

    /**
     * 加密方式
     */
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
    * @Description: 主配置方法2
    * @author tanleijin
    * @date 2019/9/10 15:03
    */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //4.配置自己实现的登录认证的service,并设置密码的加密方式()
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
    * @Description: 主配置方法1
    * @author tanleijin
    * @date 2019/9/10 15:03
    */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //1.表单登录
        http.formLogin()
                .loginPage(securityProperties.getLoginUrl())//后台登录的页面
                .loginProcessingUrl(securityProperties.getLoginProcessingUrl()) //登录请求拦截的url,也就是form表单提交时指定的action
                .usernameParameter(securityProperties.getUsernameParameter())  //用户名的请求字段 默认为userName
                .passwordParameter(securityProperties.getPasswordParameter())  //密码的请求字段 默认为password
                .successHandler(defaultAuthenticationSuccessHandler)
                .failureHandler(defaultAuthenticationFailureHandler)
                .and()
            //2.配置过滤请求
            .authorizeRequests()
            .antMatchers(getUrls()).permitAll()//不拦截请求(根据自己的需要设置不需要拦截的请求比如登录页面,js,css等)
            .and()
            .csrf().disable()//禁用csrf
            //3.记住我(当需要记住我的)
            .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getRememberMeSeconds())// 有效期20天
                .and()
            //4.配置自己实现的登录认证的service(此步骤移到主配置方法2中配置)
            //.userDetailsService(userDetailsService)
            //5.配置session管理
            .sessionManagement()
                .invalidSessionUrl(securityProperties.getLoginUrl())//session失效默认的跳转地址
                .maximumSessions(securityProperties.getMaximumSessions())//最大的并发数
                .maxSessionsPreventsLogin(securityProperties.isMaxSessionsPreventsLogin());//之后的登录是否踢掉之前的登录
            //    .expiredSessionStrategy(sessionInformationExpiredStrategy)
            //    .and()
            //   .and()
            //6.登出
            /*.logout()
                .invalidateHttpSession(true)
                .logoutUrl(securityProperties.getLogout())
                .deleteCookies(securityProperties.getJsessionid());
                //.logoutSuccessHandler(logoutSuccessHandler);*/
    }
    /**
     * 获取IndexController,ArticleController,UserDataController中的不需要拦截的请求
     * @return
     */
    private String[] getUrls() {
        List<String> urls = getUrlList();
        urls.addAll(GetMapperUtil.getMapperValue());
        return urls.stream().toArray(String[]::new);
    }
    /**
     * 获取一些静态的不需要拦截的请求
     * @return
     */
    private List<String> getUrlList(){
        List<String> urls = new ArrayList<>();
        urls.add("/favicon.ico");
        urls.add("/webjars/**");
        urls.add("/asserts/**");
        urls.add("/images/**");
        return urls;
    }
}

securityProperties

securityProperties即security配置中需要设置的一些值,没有写死在配置文件中,而写到属性文件这样方便以后在yml文件中配置。代码如下:


/**
 * @Author: tanleijin
 * @description (属性值)
 * @Date:2019/9/6 16:08
 */
@Component
public class SecurityProperties {

    /**
     * 当请求需要身份认证时,默认跳转的url
     */
    private String loginUrl = "/admin/login";
    /**
     * form表单用户名参数名
     */
    private String usernameParameter = "username";

    /**
     * form表单密码参数名
     */
    private String passwordParameter = "password";

    /**
     * 认证的url
     */
    private String loginProcessingUrl = "/security/login";
    private String logout = "/logout";

    private int rememberMeSeconds = 3600 * 24 * 20;
    /**
     * 同一个用户在系统中的最大session数,默认1
     */
    private int maximumSessions = 1;
    /**
     * 达到最大session时是否阻止新的登录请求,默认为false,不阻止,新的登录会将老的登录失效掉
     */
    private boolean maxSessionsPreventsLogin;
    private String jsessionid = "JSESSIONID";
    }

userDetailsService

userDetailsService是需要实现的登录用户查询的service接口,实现loadUserByUsername()方法

/**
* 用户登陆查询的操作
* @author tanleijin
* @date 2019/9/10 15:40
*/
@Component
public class MyUserDetailsService implements UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyUserDetailsService.class);
    
    @Autowired
    private AdminService adminService;//自己的查询用户的service

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LOGGER.info("表单登录用户名:" + username);
        AdminModel loginAdmin = adminService.getOne(new LambdaQueryWrapper<AdminModel>().eq(AdminModel::getUsername, username));
        if(loginAdmin == null) {
            throw new UsernameNotFoundException("找不到该账户信息!");//抛出异常,会根据配置跳到登录失败页面
    }
        
        LOGGER.info("username: {} , password :{}" , username , loginAdmin.getPassword());
        
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN");
        return new User(username, loginAdmin.getPassword(), true,true,true,true,authorities);
    }

}

defaultAuthenticationSuccessHandler

defaultAuthenticationSuccessHandler用户登录成功的处理器,为了返回json数据重写onAuthenticationSuccess方法

/**
* 1.自定义的登陆成功处理  implements  AuthenticationSuccessHandler  Override  onAuthenticationSuccess()
 * 2. 或者继承框架默认实现的成功处理器类 SavedRequestAwareAuthenticationSuccessHandler 重写父类方法onAuthenticationSuccess
* @author tanleijin
* @date 2019/9/10 10:54
*/
@Component
public class DefaultAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAuthenticationSuccessHandler.class);

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        LOGGER.info("----login in succcess----");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JsonUtils.objectToJson(R.ok()));
    }

}

defaultAuthenticationFailureHandler

defaultAuthenticationFailureHandler用户登录失败的处理器

/**
* 1.和登录成功处理器是一样的道理
* @author tanleijin
* @date 2019/9/10 11:09
*/
@Component
public class DefaultAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAuthenticationFailureHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException,
            ServletException {
        LOGGER.info("login in failure : " +  exception.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JsonUtils.objectToJson(R.error(exception.getMessage())));
    }
}

R返回结果封装类

其中就2个属性 code,msg

public class R extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public R() {
		put("code", 0);
		put("msg", "操作成功");
	}

	public static R error() {
		return error(1, "操作失败");
	}

	public static R error(String msg) {
		return error(500, msg);
	}

	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}

	public static R ok(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}

	public static R ok() {
		return new R();
	}

	@Override
	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

前台页面

这里我就简单贴一下代码,我用的是freemarker模板引擎
html:

 <form action="" method="post">
                    <div class="panel loginbox">
                        <div class="text-center margin-big padding-big-top">
                            <h1>DreamDo后台管理</h1>
                        </div>
                        <div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
                            <div class="form-group">
                                <div class="field field-icon-right">
                                    <input type="text" class="input input-big" name="name" id="username" placeholder="登录账号" />
                                    <span class="icon icon-user margin-small"></span>
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="field field-icon-right">
                                    <input type="password" class="input input-big" name="password" id="password"  placeholder="登录密码" />
                                    <span class="icon icon-key margin-small"></span>
                                </div>
                            </div>
<#--                            <div class="form-group">
                                <div class="field">
                                    <input id="vcode" type="text" class="input input-big" name="code" placeholder="填写右侧的验证码" />
                                    <img src="/code/image" alt="" width="100" height="32" class="passcode" style="height:43px;cursor:pointer;" onClick="this.src=this.src+'?'">
                                </div>
                            </div>-->
                        </div>
                        <div style="padding:30px;">
                            <input type="button" id="button" class="button button-block bg-main text-big input-big" value="登录" onclick="login()"/>
                        </div>
                    </div>
                </form>

js:

function login() {
	var username = $("#username").val();
	var password = $("#password").val();
	//var vcode = $("#vcode").val();
	$.ajax({
		type : "POST",
		data : {
			"username" : username,
			"password" : password
			//"imageCode" : vcode
		},
		dataType : "json",
		url : "/security/login",
		success : function(result) {
			if (result.code == '0') {
				swal({
					  icon: "success",
					  title : "登录成功,进入系统!"
				}).then((value) =>{
					window.location.href = "/admin/main";
				});
			} else {
				swal({
					  icon: "error",
					  title : result.msg
				});
			}
		}
	});
}

总结

以上就是springboot2中集成security框架,后续还会有实现记住我,验证码,qq登录,原理简单说。给自己立个小目标写个100篇文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值