登录启动类,登陆表单验证等都在这里
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationProvider provider;
@Autowired
private AuthenticationSuccess authenticationSuccess;//登录成功处理类
@Autowired
private AuthenticationFail authenticationFail;//登录失败处理类
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单验证(身份认证)
http.formLogin()
//自定义登陆页面
.loginPage("/login")
//如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
.loginProcessingUrl("/login")
.successHandler(authenticationSuccess)
.failureHandler(authenticationFail)
.and()
//请求授权
.authorizeRequests()
//在访问我们的URL时,我们是不需要省份认证,可以立即访问
//不需验证的 登录 文件夹名 小图片 注册 忘记密码 超时 druid连接池
.antMatchers("/login","/personal/**","/assets/**","/webjars/**","/**.ico","/register","/forgetPassword","/timeout","/druid/**").permitAll()
//所有请求都被拦截,跳转到(/login请求中)
.anyRequest()
//都需要我们身份认证
.authenticated()
.and()
//对应了注销相关的配置
.logout()
//退出登录 url
.logoutUrl("/logout")
//SpringSecurity保护机制
.and().csrf().disable()
.headers().frameOptions().sameOrigin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//登录处理
auth.authenticationProvider(provider);
}
}
new R(HttpStatus.OK.value(), 可以去掉
/**
* 登录成功处理类
*/
@Component
public class AuthenticationSuccess implements AuthenticationSuccessHandler{
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
request.getSession().setAttribute("loginUser", authentication.getPrincipal());
response.getWriter().print(objectMapper.writeValueAsString(new R(HttpStatus.OK.value(), "success")));
}
}
/**
* 登录失败处理类
*/
@Component
public class AuthenticationFail implements AuthenticationFailureHandler{
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json");
response.getWriter().print(objectMapper.writeValueAsString(new R(HttpStatus.INTERNAL_SERVER_ERROR.value(), exception.getLocalizedMessage())));
}
}
登录的各种验证判断都在这里,权限这里有写另一篇文章详细介绍。
/**
* 登录逻辑类
* @author Administrator
*
*/
@Component
public class SecurityAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 用户名
String userName = authentication.getName();
// 密码
String password = (String) authentication.getCredentials();
// 登录
UserInfo userInfo = (UserInfo) securityUserService.loadUserByUsername(userName);
//各种判断,根据软件自己添加start
//用户是否存在
if (userInfo == null) {
throw new UsernameNotFoundException("none");
}
// 密码判断 输入的密码 加密后的密码
if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
throw new BadCredentialsException("error");
}
// 用户状态 0 正常 1 禁用
if (userInfo.getStatus().equals("1")) {
throw new DisabledException("disabled");
}
// 获取权限
String auth = authorityRepository.findAuthorityByCustomerIdAndUserNameAndIdenti(userInfo.getCustomeCd(), userInfo.getUsername(), userInfo.getIdenti());
userInfo.setAuthority(getFunction(auth));
List<GrantedAuthority> lists = new ArrayList<GrantedAuthority>();
getRolesAndAuthority(userInfo, lists);
//各种判断,根据软件自己添加end
// 用户类 权限列表
return new UsernamePasswordAuthenticationToken(userInfo, password, lists);
}
@Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return true;
}
}
登录检索数据库的在这里,判断用户名密码是否一致,检索出用户的所有信息后放到info中,之后在进入登陆逻辑类中,然后进入登录启动类中,最后把info的信息放到登录成功处理类的loginUser中,这样前台就可以使用th:object="${session.loginUser}"获取信息,为一些控件赋值,eg:th:text="*{name}"
@Component
public class SecurityUserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@SysLog("登录")
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUserName(username);//检索出用户所有信息
if (user != null) {
String userType = null;
if (user.getRoleCode().equals("0"))
userType = Constants.SUPER_ADMIN;
else if (user.getRoleCode().equals("1"))
userType = Constants.ADMIN;
else if (user.getRoleCode().equals("2"))
userType = Constants.OPERATOR;
else if (user.getRoleCode().equals("3"))
userType = Constants.TESTEE;
UserInfo info = new UserInfo(user.getUserId(),user.getUserName(), user.getPassword(), user.getName(), user.getSex(), user.getAge(),
user.getStatus(), user.getDelFlag(), user.getRoleCode(), user.getIdentifier(), user.getCustomerId(),
userType,true, true, true, user.getStatus() == "0" ? true : false);
//把信息都放到info中
return info;
}
return null;
}
}
登录逻辑因为是特殊的调用,所以登录的用户id和密码的名字需要写成username和password,这样在登录逻辑类中才可以接到值,在注册等别的业务的时候就没有必须规定了,写成啥看自己。
<form method="post" id="login">
<input name="username" id="username" type="text" placeholder="请输入用户ID" maxlength="15">
<input name="password" type="password" id="password" placeholder="请输入密码" autocomplete="off" maxlength="15"/>
<input value="登录" style="width: 45%;" id="sub" type="button">
</form>
登录的js
$.ajax({
url: "/login",
type: "post",
dataType: "JSON",
data: $("#login").serialize(),
success: function(data) {
if (data.msg == "success") {
window.location.href = "/home"
} else {
if (data.msg == "error") {
layer.msg("用户名或密码错误")
} else {
if (data.msg == "disabled") {
layer.msg("用户已被冻结")
} else {
if (data.msg == "none") {
layer.msg("用户不存在")
} else {
layer.msg("登录失败")
}
}
}
}
},
error: function() {
layer.msg("登录失败", {
icon: 2,
time: 1000
})
}
})
其中window.location.href = “/home”
意思是跳转到首页index.html中,在PageController 中写跳转的内容
@Controller
public class PageController {
@GetMapping("/login")
public String loginPage() {
return "login";
}
@GetMapping("/home")
public String index() {
return "index";
}
}