从零起步基于ElasticSearch的搜房网(前后端集成)实战(介绍与整体目录)点击即可
静态资源集成太多页面,我已经上传到博客资源链接,供下载。后期代码全部完善后,会上传到github上。
静态资源链接下载(点击)
业务与功能分析:
本章主要实现的功能:
- 后台登录
- 权限控制
- 注销功能
使用基于hui的后台管理模板
开始代码逻辑:
首先在controller中新建一个admin的package,然后再admin文件夹中新建一个AdminController的类。
AdminController:
package liangliang.bigdata.web.controller.admin;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminController {
@GetMapping("/admin/center")
public String AdminCenterPage(){
return "admin/center";
}
@GetMapping("/admin/welcome")
public String AdminWelcomePage(){
return "admin/welcome";
}
@GetMapping("/admin/login")
public String AdminLoginPage(){
return "admin/login";
}
}
在templates文件夹中新建admin文件夹,在admin中新建三个html页面:center.html、welcome.html、login.html
center.html:
<!--_meta 作为公共模版分离出去-->
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<div th:include="admin/common :: head"></div>
<div th:include="admin/common :: header"></div>
<body>
<div th:include="admin/common :: left_menu"></div>
<section class="Hui-article-box">
<div id="Hui-tabNav" class="Hui-tabNav hidden-xs">
<div class="Hui-tabNav-wp">
<ul id="min_title_list" class="acrossTab cl">
<li class="active">
<span title="我的桌面" data-href="welcome.html">我的桌面</span>
<em></em></li>
</ul>
</div>
<div class="Hui-tabNav-more btn-group"><a id="js-tabNav-prev" class="btn radius btn-default size-S"
href="javascript:;"><i class="Hui-iconfont"></i></a><a
id="js-tabNav-next" class="btn radius btn-default size-S" href="javascript:;"><i class="Hui-iconfont"></i></a>
</div>
</div>
<div id="iframe_box" class="Hui-article">
<div class="show_iframe">
<div style="display:none" class="loading"></div>
<iframe scrolling="yes" frameborder="0" src="welcome.html"></iframe>
</div>
</div>
</section>
<div class="contextMenu" id="Huiadminmenu">
<ul>
<li id="closethis">关闭当前</li>
<li id="closeall">关闭全部</li>
</ul>
</div>
<div th:include="admin/common :: footer">
</div>
<!--请在下方写此页面业务相关的脚本-->
<script type="text/javascript" src="/static/lib/jquery.contextmenu/jquery.contextmenu.r2.js"></script>
<div th:include="admin/common :: house_manage"></div>
</body>
</html>
login.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>寻屋-后台登录系统</title>
<!--- CSS --->
<link rel="stylesheet" href="/static/css/admin/login-style.css" type="text/css" />
<!--- Javascript libraries (jQuery and Selectivizr) used for the custom checkbox --->
<!--[if (gte IE 6)&(lte IE 8)]>
<script type="text/javascript" src="/static/lib/jquery/1.9.1/jquery.js"></script>
<script type="text/javascript" src="/static/js/admin/selectivizr.js"></script>
<noscript><link rel="stylesheet" href="/static/css/admin/login-fallback.css" /></noscript>
<![endif]-->
</head>
<body>
<div id="container">
<form action="#" th:action="@{/login}" method="post">
<div class="login">管理员登录</div>
<div th:if="${param.error}" style="position: absolute; color: red; padding-top: 6%; padding-left: 3%">
非法的用户名或密码
</div>
<div class="username-text">用户名:</div>
<div class="password-text">密码:</div>
<div class="username-field">
<input type="text" name="username" placeholder="请输入用户名"/>
</div>
<div class="password-field">
<input type="password" name="password" placeholder="请输入密码" />
</div>
<!--<input type="checkbox" name="remember-me" id="remember-me" /><label for="remember-me">Remember me</label>-->
<!--<div class="forgot-usr-pwd">Forgot <a href="#">username</a> or <a href="#">password</a>?</div>-->
<div class="login-button">
<button type="submit" class="login_button">登录</button>
</div>
</form>
</div>
<div id="footer">
寻屋-后台管理系统 <a href="#" target="_blank" title="登录"></a>
</div>
</body>
</html>
welcome.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="admin/common :: head">
<title>我的桌面</title>
</head>
<body>
<div class="page-container">
<p style="height: 50px;" class="f-20 text-success">欢迎来到七天共享云平台 <span class="f-14">^_^</span>祝亲共享愉快!</p>
<div><img style="text-align: center" src="/static/images/day_welcom.gif" height="100%" width="100%"></div>
</div>
</body>
</html>
先访问我们的路由:http://127.0.0.1:8080/admin/login
整体模块下图:
在WebMvcConfig中添加支持SpringSecurity的方言:
WebMvcConfig:
package liangliang.bigdata.config;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
@Configuration
//帮助我们获取spring的上下文
public class WebMvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
//私有化一个类
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//配置静态资源
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
/**
* 对模板资源进行解析
* 模板资源解析器
*/
@Bean
//做一个前缀绑定,否则找不到路径
@ConfigurationProperties(prefix = "spring.thymeleaf")
public SpringResourceTemplateResolver templateResolver (){
//实例化一个
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
//设置spring的上下文
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
/**
* Thymeleaf标准方言解析器
*/
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
//配置资源解析器的引擎
templateEngine.setTemplateResolver(templateResolver());
//设置支持EL表达式
templateEngine.setEnableSpringELCompiler(true);
//支持SpringSecurity方言
SpringSecurityDialect springSecurityDialect = new SpringSecurityDialect();
templateEngine.addDialect(springSecurityDialect);
return templateEngine;
}
/**
* 视图解析器
*/
@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
//配置Thymeleaf标准方言解析器
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
在config的文件夹中新建一个 WebSecurityConfig类:
WebSecurityConfig:
package liangliang.bigdata.config;
import liangliang.bigdata.security.AuthProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* HTTP权限控制
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
//资源访问权限
httpSecurity.authorizeRequests().antMatchers("/admin/login").permitAll()//管理员登录入口
.antMatchers("/static/**").permitAll()//静态资源
.antMatchers("user/login").permitAll()//用户登录入口
.antMatchers("/admin/**").hasAnyRole("ADMIN","USER")
.antMatchers("/api/user/**").hasAnyRole("ADMIN","USER")
.and()
.formLogin()
.loginProcessingUrl("/login")//配置角色登录处理入口
.and();
httpSecurity.csrf().disable();//防御策略
httpSecurity.headers().frameOptions();
}
/**
* 自定义认证策略
* @EnableWebSecurity
* @EnableGlobalMethodSecurity
* 在以上这两个任意一个下面只有这个
* AuthenticationManagerBuilder
*
*/
@Autowired
public void configGlobal(AuthenticationManagerBuilder auth){
// 配置内存用户名和密码
auth.authenticationProvider(authProvider()).eraseCredentials(true);
}
@Bean
public AuthProvider authProvider(){
return new AuthProvider();
}
}
在repository中新建一个借口(RoleRepository):
RoleRepository:
package liangliang.bigdata.repository;
import liangliang.bigdata.entity.Role;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
//一个对应实体类,一个对应我们的id
/**
* 角色数据DAO层
*/
public interface RoleRepository extends CrudRepository<Role,Long> {
List<Role> findRoleByUserId (Long userId);
}
新建一个security文件夹,在里面新建一个AuthProvider类:
AuthProvider :
package liangliang.bigdata.security;
import liangliang.bigdata.entity.User;
import liangliang.bigdata.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
* 自定义认证实现
*/
public class AuthProvider implements AuthenticationProvider {
private final Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
@Autowired
private IUserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String inputPassword = (String) authentication.getCredentials();
User user = userService.findUserByName(userName);
if (user == null) {
throw new AuthenticationCredentialsNotFoundException("用户不存在");
}
//使用用户id作为加盐
if (this.passwordEncoder.isPasswordValid(user.getPassword(), inputPassword, user.getId())) {
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
}
throw new BadCredentialsException("authError");
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
新建一个service文件夹在service从中新建一个user文件夹,并且在service新建一个IUserService接口:
IUserService:
package liangliang.bigdata.service;
import liangliang.bigdata.entity.User;
/**
* 用户服务
*/
public interface IUserService {
User findUserByName (String userName);
}
在service的user文件夹中新建一个UserServiceImpl类:
UserServiceImpl :
package liangliang.bigdata.service.user;
import liangliang.bigdata.entity.Role;
import liangliang.bigdata.entity.User;
import liangliang.bigdata.repository.RoleRepository;
import liangliang.bigdata.repository.UserRepository;
import liangliang.bigdata.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
//user的包装
@Override
public User findUserByName(String userName) {
User user = userRepository.findByName(userName);
if(user == null){
return null;
}
List<Role> roles = roleRepository.findRoleByUserId(user.getId());
if (roles == null){
throw new DisabledException("权限非法");
}
List<GrantedAuthority>authorities = new ArrayList<>();
roles.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName())));
return user;
}
}
在entity中的两个实体类,一个是User一个是Role:
User:
package liangliang.bigdata.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Entity
//注意数据库中的user是小写的,需要注意一下与数据库中做一个映射
@Table(name = "user")
public class User implements UserDetails {
@Id
//因为同时兼容hibernate和H2
@GeneratedValue(strategy = GenerationType.IDENTITY)
//主键
private Long id;
//姓名
private String name;
//密码
private String password;
//邮箱
private String email;
//电话号码 做一个映射与数据库
@Column(name = "phone_number")
private String phoneNumber;
//状态码
private int status;
//创建时间
@Column(name="create_time")
private Date createTime;
//最后登录时间
@Column(name = "last_login_time")
private Date lastLoginTime;
@Column(name = "last_update_time")
private Date lastUpdateTime;
//头像
private String avatar;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return name;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setPassword(String password) {
this.password = password;
}
//权限给一个新的属性
//为了让他透明,不需要验证这个字段
@Transient
private List<GrantedAuthority> authorityList;
public List<GrantedAuthority>getAuthorityList(){
return authorityList;
}
public void setAuthorityList(List<GrantedAuthority> authorityList) {
this.authorityList = authorityList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
public Date getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(Date lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
}
Role:
package liangliang.bigdata.entity;
import javax.persistence.*;
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_id")
private Long userId;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
index.html:
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta http-equiv="Cache-Control" content="no-transform"/>
<meta http-equiv="Cache-Control" content="no-siteapp"/>
<meta http-equiv="Content-language" content="zh-CN"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="applicable-device" content="pc">
<title>寻屋</title>
<meta name="description" content="方便快捷寻屋"/>
<meta name="keywords" content="寻屋"/>
<link href="/static/css/main.css" rel='stylesheet' type='text/css'/>
<link href="/static/css/index.css" rel='stylesheet' type='text/css'/>
</head>
<body>
<header th:replace="common :: header"></header>
<div class="city-change animated bounceIn" style="display: block;">
<span class="close"></span>
<div class="title">选择城市
<span class="city-tab">
<span class="code1">热门城市:</span>
<a href="/rent/house?cityEnName=bj" title="北京租房">北京</a>
<a href="/rent/house?cityEnName=sh" title="上海租房">上海</a>
<a href="/rent/house?cityEnName=gz" title="广州租房">广州</a>
<a href="/rent/house?cityEnName=sz" title="深圳租房">深圳</a>
</span>
</div>
<div class="title-line"></div>
<div class="fc-main clear">
<div class="fl citys-l">
<ul>
<li class="clear"><span class="code-title fl">B</span>
<div class="city-enum fl"><a href="/rent/house?cityEnName=bj" title="北京租房">北京</a></div>
</li>
<li class="clear"><span class="code-title fl">C</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=cd" title="成都租房">成都</a>
<a href="/rent/house?cityEnName=cq" title="重庆租房">重庆</a>
<a href="/rent/house?cityEnName=cs" title="长沙租房">长沙</a>
</div>
</li>
<li class="clear"><span class="code-title fl">D</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=dl" title="大连租房">大连</a>
<a href="/rent/house?cityEnName=dg" title="东莞租房">东莞</a>
</div>
</li>
<li class="clear"><span class="code-title fl">F</span>
<div class="city-enum fl"><a href="/rent/house?cityEnName=fs" title="佛山租房">佛山</a></div>
</li>
<li class="clear"><span class="code-title fl">G</span>
<div class="city-enum fl"><a href="/rent/house?cityEnName=gz" title="广州租房">广州</a></div>
</li>
<li class="clear"><span class="code-title fl">H</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=hz" title="杭州租房">杭州</a>
<a href="/rent/house?cityEnName=hui" title="惠州租房">惠州</a>
<a href="/rent/house?cityEnName=hk" title="海口租房">海口</a>
<a href="/rent/house?cityEnName=hf" title="合肥租房">合肥</a>
</div>
</li>
</ul>
</div>
<div class="fl citys-r">
<ul>
<li class="clear">
<span class="code-title fl">L</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=ls" title="陵水租房">陵水</a>
<a href="/rent/house?cityEnName=lf" title="廊坊租房">廊坊</a>
</div>
</li>
<li class="clear">
<span class="code-title fl">N</span>
<div class="city-enum fl"><a href="/rent/house?cityEnName=nj" title="南京租房">南京</a></div>
</li>
<li class="clear">
<span class="code-title fl">Q</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=qd" title="青岛租房">青岛</a>
<a href="/rent/house?cityEnName=qh" title="琼海租房">琼海</a>
</div>
</li>
<li class="clear">
<span class="code-title fl">S</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=sh" title="上海租房">上海</a>
<a href="/rent/house?cityEnName=sz" title="深圳租房">深圳</a>
<a href="/rent/house?cityEnName=su" title="苏州租房">苏州</a>
<a href="/rent/house?cityEnName=sjz" title="石家庄租房">石家庄</a>
<a href="/rent/house?cityEnName=sy" title="沈阳租房">沈阳</a>
<a href="/rent/house?cityEnName=sanya" title="三亚租房">三亚</a>
</div>
</li>
<li class="clear">
<span class="code-title fl">T</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=tj" title="天津租房">天津</a>
<a href="/rent/house?cityEnName=ty" title="太原租房">太原</a>
</div>
</li>
<li class="clear">
<span class="code-title fl">W</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=wh" title="武汉租房">武汉</a>
<a href="/rent/house?cityEnName=wx" title="无锡租房">无锡</a>
<a href="/rent/house?cityEnName=wc" title="文昌租房">文昌</a>
<a href="/rent/house?cityEnName=wn" title="万宁租房">万宁</a>
</div>
</li>
<li class="clear">
<span class="code-title fl">X</span>
<div class="city-enum fl">
<a href="/rent/house?cityEnName=xm" title="厦门租房">厦门</a>
<a href="/rent/house?cityEnName=xa" title="西安租房">西安</a>
<a href="/rent/house?cityEnName=xsbn" title="西双版纳租房">西双版纳</a></div>
</li>
</ul>
</div>
</div>
</div>
<div th:include="common :: footer"></div>
<script th:inline="javascript" type="text/javascript">
$(function () {
$('.xunwu-header .index-page').addClass('on');
function msgTip(content) {
layer.open({
type: 1,
offset: '100px',
area: ['420px', '240px'],
skin: 'layui-layer-lan', //样式类名
closeBtn: 1, //显示关闭按钮
anim: 2,
shadeClose: true, //开启遮罩关闭
content: content,
time: 2000
});
}
var msg = [[${msg}]];
if (msg === 'must_chose_city') {
msgTip('<p class="msgTip">你必须选择一个城市</p>');
} else if (msg === 'not_support_city') {
msgTip('<p class="msgTip">暂不支持所选城市</p>');
} else {
// TODO
}
});
</script>
</body>
</html>
现在我们可以运行看一下效果:密码:admin,账号admin
后台管理模块
- 后台整体界面
- 登录功能
- 角色权限控制
整体模块如下图所示:
更新后的WebSecurityConfig:
package liangliang.bigdata.config;
import liangliang.bigdata.security.AuthProvider;
import liangliang.bigdata.security.LoginAuthFailHander;
import liangliang.bigdata.security.LoginUrlEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
/**
* Http权限控制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
//资源访问权限
http.authorizeRequests()
.antMatchers("/admin/login").permitAll()//管理员登录入口
.antMatchers("/static/**").permitAll()//静态资源
.antMatchers("/user/login").permitAll()//用户登录入口
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("ADMIN","USER")
.antMatchers("/api/user/**").hasAnyRole("ADMIN","USER")
.and()
.formLogin()
.loginProcessingUrl("/login")//配置之角色登录处理入口
.failureHandler(authFailHandler())
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/logout/page")
.deleteCookies("JESSIONID")
.invalidateHttpSession(true)
.and()
.exceptionHandling()
.authenticationEntryPoint(urlEntryPoint())
.accessDeniedPage("/403");
http.csrf().disable();
http.headers().frameOptions().sameOrigin();
}
/**
* 自定义认证策略
* @throws Exception
*/
@Autowired
public void configGlobal(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and();
auth.authenticationProvider(authProvider()).eraseCredentials(true);
}
@Bean
public AuthProvider authProvider() {
return new AuthProvider();
}
@Bean
public LoginUrlEntryPoint urlEntryPoint() {
return new LoginUrlEntryPoint("/user/login");
}
@Bean
public LoginAuthFailHander authFailHandler() {
return new LoginAuthFailHander(urlEntryPoint());
}
@Bean
public AuthenticationManager authenticationManager() {
AuthenticationManager authenticationManager = null;
try {
authenticationManager = super.authenticationManager();
} catch (Exception e) {
e.printStackTrace();
}
return authenticationManager;
}
// @Bean
// public AuthFilter authFilter() {
// AuthFilter authFilter = new AuthFilter();
// authFilter.setAuthenticationManager(authenticationManager());
// authFilter.setAuthenticationFailureHandler(authFailHandler());
// return authFilter;
// }
}
基于角色登录入口的控制器:
LoginUrlEntryPoint:
package liangliang.bigdata.security;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* 基于角色的登录入口控制器
*/
public class LoginUrlEntryPoint extends LoginUrlAuthenticationEntryPoint {
private static final String API_FREFIX = "/api";
private static final String API_CODE_403 = "{\"code\": 403}";
private static final String CONTENT_TYPE = "application/json;charset=UTF-8";
private PathMatcher pathMatcher = new AntPathMatcher();
private final Map<String, String> authEntryPointMap;
public LoginUrlEntryPoint(String loginFormUrl) {
super(loginFormUrl);
authEntryPointMap = new HashMap<>();
// 普通用户登录入口映射
authEntryPointMap.put("/user/**", "/user/login");
// 管理员登录入口映射
authEntryPointMap.put("/admin/**", "/admin/login");
}
/**
* 根据请求跳转到指定的页面,父类是默认使用loginFormUrl
* @param request
* @param response
* @param exception
* @return
*/
@Override
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
String uri = request.getRequestURI().replace(request.getContextPath(), "");
for (Map.Entry<String, String> authEntry : this.authEntryPointMap.entrySet()) {
if (this.pathMatcher.match(authEntry.getKey(), uri)) {
return authEntry.getValue();
}
}
return super.determineUrlToUseForThisRequest(request, response, exception);
}
}
登录验证失败处理器
LoginAuthFailHander:
package liangliang.bigdata.security;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录验证失败处理器
* Created by 瓦力.
*/
public class LoginAuthFailHander extends SimpleUrlAuthenticationFailureHandler {
private final LoginUrlEntryPoint urlEntryPoint;
public LoginAuthFailHander(LoginUrlEntryPoint urlEntryPoint) {
this.urlEntryPoint = urlEntryPoint;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String targetUrl =
this.urlEntryPoint.determineUrlToUseForThisRequest(request, response, exception);
targetUrl += "?" + exception.getMessage();
super.setDefaultFailureUrl(targetUrl);
super.onAuthenticationFailure(request, response, exception);
}
}
运行一下看效果,其他没更新的就是,暂时不需要改变的。
管理员登录中心:
用户登录:
验证失败逻辑显示:
用户登录中心:
上篇博客:基于ElasticSearch的搜房网实战(第四章第四章环系统架构设计与分层)
下篇博客:基于ElasticSearch的搜房网实战(第六章房源信息管理模块实现)