springboot2集成security实现自定义页面登录
security的基本介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,跟前面写的shiro其实是相同功能。核心功能:
- 认证(你是谁)
- 授权(你能干什么)
- 攻击防护(防止伪造身份)
前提说明
首先搭好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篇文章。