本文同步发布在是与非博客(www.zhuoerhuobi.cn)
随着是与非博客基础功能的完善,给我的网站加上用户认证授权系统这件事终于是提上日程了。
用户系统我们可以拆解为登录认证和授权访问这两个功能。我们先登陆账号,用户的状态就存在在网站了,然后网站再根据该用户的角色权限授权该用户访问某些页面和接口。
如果我们自己动手实现用户系统,可以料想到大致流程为:
读取用户输入的账户密码 --> 加密后和数据库中的用户表进行比对 --> 若比对成功,则读取数据库中用户的信息,登陆成功 --> 在我们已有的接口或页面上添加判断条件或拦截器,根据用户信息中的角色或者权限等字段判断是否让用户通行。
这么做的缺点也很明显,工作量大,并且认证系统和我们的业务代码高度耦合,不利于后期维护。
在这种情况下,用户系统相关框架应运而生,当下最流行的shrio和springsecurity都能很好的完成我们上面的需求,并且它们遵循了aop原则,并不需要更改我们的业务代码就可以加入用户系统功能,相当方便。
要引入springsecurity功能,我们首先要在pom文件中添加依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
这样,项目就引入了security模块。security默认拦截所有请求,而默认登录用户名为user,默认密码在服务启动时会打印在终端下。
security相关配置继承自WebSecurityConfigurerAdapter类,所以我们定义自己的MySecurityConfig类继承WebSecurityConfigurerAdapter,并重写其中几个方法。
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyUserDetailService myUserDetailService;
@Override
public void configure(WebSecurity web) throws Exception {
//不拦截静态资源
web.ignoring().antMatchers("/css/**","/js/**","/fonts/**","/lib/**","/img/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();/*关闭防御csrf攻击功能*/
//链式编程,可以连写若干个功能,为了方便理解,我们还是拆开来写。
http.formLogin() /*开启表单登录*/
.loginPage("/login")/*自定义我们自己的登陆页面*/
.defaultSuccessUrl("/index?page=1").permitAll()/*自定义登录成功页面*/
.and().authorizeRequests()
.antMatchers("/login","/resources/**").permitAll()/*选择哪些页面不需要权限*/
.antMatchers("/admin/**").hasRole("ADMIN");/*选择哪些页面必须要相应角色*/
http.logout().logoutSuccessUrl("/");/*开启注销功能*/
http.rememberMe().rememberMeParameter("rememberMe");/*开启记住我功能,这里的参数要和前端相应的参数name相同*/
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//认证规则里的用户从我们自定义的MyUserDetailService里读取,并用BCryptPasswordEncoder加密
auth.userDetailsService(myUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
}
其中我们自定义的MyUserDetailService从数据库中读取用户信息:
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
MyUserMapper myUserMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = myUserMapper.getUserByName(username);
if (user != null) {
return new User(user.getName(),user.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));
}
else {
throw new UsernameNotFoundException("用户\""+username+"\"不存在!");
}
}
}
为了给用户赋予角色,我们还要在用户表添加role字段,注意role要有ROLE_ 的前缀。
接下来我们来完成前端部分,其中用户名输入框的name属性设置为username,密码输入框设置为password
<form class="form-horizontal" action="/login" method="post">
<span class="heading">用户登录span>
<div class="form-group">
<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="用户名或电子邮件">
<i class="fa fa-user">i>
div>
<div class="form-group help">
<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="密 码">
<i class="fa fa-lock">i>
<a href="#" class="fa fa-question-circle">a>
div>
<div class="form-group">
<div class="main-checkbox">
<input type="checkbox" id="checkbox1" name="rememberMe"/>
<label for="checkbox1">label>
div>
<span class="text">Remember mespan>
<button type="submit" class="btn btn-default">登录button>
div>
form>
由于前端我们使用的是thymeleaf,而thymeleaf和security是有合作的包,所以我们再导入一个依赖方便我们操作前端:
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
<version>3.0.4.RELEASEversion>
dependency>
引入后我们就可以以sec:前缀来书写动态的html标签,类似于th:
<div class="pull-right login-nav">
<ul>
<li class="dropdown" sec:authorize="isAuthenticated()"> <a sec:authentication="name" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" style="color: white"><span class="caret">span>a>
<ul class="dropdown-menu dropdown-menu-left">
<li sec:authorize="hasRole('ADMIN')"><a title="进入后台管理系统" href="/admin/article">进入后台a>li>
<li><a title="查看或修改个人信息" data-toggle="modal" data-target="#seeUserInfo">个人信息a>li>
<li><a title="查看您的登录记录" data-toggle="modal" data-target="#seeUserLoginlog">登录记录a>li>
ul>
li>
<li sec:authorize="isAuthenticated()"><a href="/logout" onClick="if(!confirm('是否确认退出?'))return false;"><button class="btn-login">退出登录button>a>li>
<li sec:authorize="!isAuthenticated()"><a href="/login"><button class="btn-login" href="/login">请登录button>a>li>
ul>
div>
这样,我们就实现了简单的登录认证功能,并对后台的接口和页面加以限制,只有管理员用户可以访问。具体效果可以在是与非博客进行体验。
这篇文章只是简单记录了我的springsecurity使用过程,其中还有很多坑和源码相关知识。如果看了这篇博文后你还想深入了解security的原理,请上网百度,或者我自己研究明白了或许会再出一篇文章专门讲解(不大可能)。