使用spring security登陆的时候一直报这个错Caused by: org.springframework.security.authentication.BadCredentialsException: Bad credentials
这是因为表里的密码是StandardPasswordEncoder()
加密方式,而application.yml
根据dev、prod环境来使用一种加密方式。如果测试环境也是使用
StandardPasswordEncoder()
加密方式,而后另一位同事不知情的情况下改为NoOpPasswordEncoder.getInstance()
加密方式就会导致request.login(userId, pwd);
登陆失败
//密码加密对象
@Bean
public PasswordEncoder passwordEncoder() {
if (envActive.equals("prod")) { //生产环境下加密方式
return new StandardPasswordEncoder();
} else {
// return NoOpPasswordEncoder.getInstance(); //测试环境加密方式
return new StandardPasswordEncoder();
}
}
SecurityController
package com.cicdi.servertemplate.modules.sys.controller;
import com.cicdi.servertemplate.modules.sys.model.LoginInfo;
import com.cicdi.servertemplate.common.config.security.ISpringSecurityService;
import com.cicdi.servertemplate.common.config.security.SpringCacheModuleSecurity;
import com.cicdi.servertemplate.common.config.security.SpringCacheRoleHierarchy;
import com.cicdi.servertemplate.common.exception.BusinessException;
import com.cicdi.servertemplate.common.model.ResponseObj;
import com.cicdi.servertemplate.common.model.RetCode;
import com.cicdi.servertemplate.common.util.VCodeUtil;
import com.cicdi.servertemplate.modules.sys.dao.ITokenMapper;
import com.cicdi.servertemplate.modules.sys.dao.IUserMapper;
import com.cicdi.servertemplate.modules.sys.model.LoginInfo;
import com.cicdi.servertemplate.modules.sys.model.Token;
import com.cicdi.servertemplate.modules.sys.model.UserModel;
import com.cicdi.servertemplate.modules.sys.service.SecurityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Created by LOG on 2017/3/27.
*/
@RestController
@RequestMapping("/security")
public class SecurityController {
@Autowired
public SecurityService securityService;
private Logger log = LoggerFactory.getLogger(SecurityController.class);
/**
* 修改密码
*
* @param userId 用户ID
* @param newPassword 新密码
* @return
*/
@RequestMapping(value = "/changePwd", method = RequestMethod.POST)
public ResponseObj<Boolean> changePwd(@RequestParam String userId,
@RequestParam String oldPassword,
@RequestParam String newPassword) throws BusinessException {
boolean result = securityService.changePassword(userId, oldPassword, newPassword);
return new ResponseObj<>(result, RetCode.SUCCESS);
}
/**
* 通过密码登录
* @param pwd 密码
* @param userId 用户名
* @param deviceId 设备ID,
* @param vcode 验证码
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value = "/loginByPwd", method = RequestMethod.POST)
public ResponseObj<LoginInfo> loginByPwd(@RequestParam("password") String pwd,
@RequestParam String userId,
@RequestParam String deviceId,
@RequestParam(required = false) String vcode,
HttpServletRequest request) throws Exception {
//有时候存在带着上一次的sessionId来访问服务导致无法登陆的问题, 也就是重复登录的问题,这里临时这样解决
// mustNoLogin(request);
// if (session.getAttribute("vcode") == null || !session.getAttribute("vcode").equals(vcode.toLowerCase())) {
// throw new BusinessException("图片验证码输入错误");
// }
//防止重复登录
if(!(SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserModel)) {
if (pwd == null || pwd.equals("")) {
throw new BusinessException("用户密码不能为空");
}
request.login(userId, pwd);
}
UserModel user = (UserModel)
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Token newToken = new Token();
newToken.setUserId(user.getUserId());
newToken.setAppCode(deviceId);
String token = securityService.genToken(newToken);
return new ResponseObj<>(new LoginInfo(user, token), RetCode.SUCCESS);
}
@RequestMapping(value = "/loginByToken", method = RequestMethod.POST)
public ResponseObj<LoginInfo> loginWithToken(@RequestParam String userId,
@RequestParam String deviceId,
@RequestParam String token,
HttpServletRequest request) throws BusinessException {
//mustNoLogin(request);
Token tokenModel = securityService.getToken(token, userId, deviceId);
if (tokenModel == null) {
throw new BusinessException("凭证无效, 自动登录失败");
}
if (new Date().getTime() - tokenModel.getCreateTime() < 1000 * 60 * 60 * 24 * 15) {
securityService.loginAnyCondition(userId);
UserModel user = (UserModel) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Token newToken = new Token();
newToken.setUserId(userId);
newToken.setAppCode(deviceId);
String genToken = securityService.genToken(newToken);
return new ResponseObj<>(new LoginInfo(user, genToken), RetCode.SUCCESS);
} else {
throw new BusinessException("凭证超时,请重新登陆");
}
}
/**
* 退出系统
*
* @param request
* @return
*/
@RequestMapping(value = "/logout", method = RequestMethod.POST)
public ResponseObj<Boolean> logout(HttpServletRequest request) throws Exception {
UserModel user = (UserModel) SecurityContextHolder
.getContext().getAuthentication().getPrincipal();
request.logout();
securityService.deleteToken(user.getUserId());
return new ResponseObj<>(true, RetCode.SUCCESS);
}
/**
* 更新权限
*
* @return
*/
@RequestMapping(value = "/updateAuthority", method = RequestMethod.GET)
public ResponseObj<Boolean> updateAuthority() {
securityService.updateAuthority();
return new ResponseObj<>(true, RetCode.SUCCESS);
}
/**
* 获取验证码接口
*
*
* @param request
* @param response
* @throws Exception
*/
@RequestMapping(value = "/vcode", method = RequestMethod.GET)
public void getVCode(HttpServletRequest request, HttpServletResponse response) throws
Exception {
mustNoLogin(request);
HttpSession session = request.getSession();
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
String verifyCode = VCodeUtil.generateVerifyCode(4);
log.debug(request.getRemoteAddr() + "获取验证码" + verifyCode);
session.setAttribute("vcode", verifyCode.toLowerCase());
int w = 100, h = 30;
VCodeUtil.outputImage(w, h, response.getOutputStream(), verifyCode);
}
private void mustNoLogin(HttpServletRequest request) {
//有时候存在带着上一次的sessionId来访问服务导致无法登陆的问题, 也就是重复登录的问题,这里临时这样解决
try {
if (SecurityContextHolder.getContext().getAuthentication().isAuthenticated()
&& !(SecurityContextHolder.getContext().getAuthentication() instanceof
AnonymousAuthenticationToken))
request.logout();
} catch (Exception ex) {
}
}
private boolean hasLogin(HttpServletRequest request){
if (SecurityContextHolder.getContext().getAuthentication().isAuthenticated() &&
SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserModel){
return true;
}
return false;
}
}
SecurityConfig
package com.cicdi.servertemplate.common.config.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.access.vote.RoleHierarchyVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import java.util.ArrayList;
import java.util.List;
/**
* Created by LOG on 2017/3/6.
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//PermissionEvaluator
@Value("${spring.profiles.active}")
protected String envActive;
@Autowired
private SpringUserDetailsService userDetailsService;
@Autowired
SpringCacheModuleSecurity springCacheModuleSecurity;
@Autowired
SpringCacheRoleHierarchy springCacheRoleHierarchy;
//密码加密对象
@Bean
public PasswordEncoder passwordEncoder() {
if (envActive.equals("prod")) { //生产环境下加密方式
return new StandardPasswordEncoder();
} else {
// return NoOpPasswordEncoder.getInstance();
return new StandardPasswordEncoder();
}
}
//表达式
@Bean
public DefaultWebSecurityExpressionHandler expressionHandler() {
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy());
return expressionHandler;
}
//决策管理 Voter
//如果mrVoter否决了, 则直接否决,
//mrVoter不表决或者同意了。 则进行webExpressionVoter表决, 即下方代码configure配置
//一般configure配置的 数据库表中就不应该再配置了, 否则数据库表中配置了, 则configure就不要配置了
//通常 configure 只是检验是否登陆和 静态资源全通过
@Bean
@SuppressWarnings(value = {"rawtypes"})
public UnanimousBased accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends
Object>>();
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(expressionHandler());
MRVoter mrVoter = new MRVoter();
mrVoter.setRoleHierarchy(roleHierarchy());
mrVoter.setSpringCacheModuleSecurity(springCacheModuleSecurity);
decisionVoters.add(mrVoter);
decisionVoters.add(webExpressionVoter);
UnanimousBased unanimousBased = new UnanimousBased(decisionVoters);
unanimousBased.setAllowIfAllAbstainDecisions(false);
return unanimousBased;
}
public RoleHierarchyVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy());
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
return springCacheRoleHierarchy;
}
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//.authenticationProvider(new SpringAuthenticationProvider())
//.eraseCredentials(false)
.userDetailsService(userDetailsService)
//对password进行加密
.passwordEncoder(passwordEncoder());
}
@Bean
public Http401AuthenticationEntryPoint securityException401EntryPoint() {
return new Http401AuthenticationEntryPoint("");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
expressionInterceptUrlRegistry = http.exceptionHandling().authenticationEntryPoint
(securityException401EntryPoint())
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.sameOrigin()
.and()
.logout().addLogoutHandler(new SecurityContextLogoutHandler())
.and()
.authorizeRequests()
// .antMatchers(HttpMethod.POST, "/v1/ftpConfig/test").permitAll()
// .antMatchers(HttpMethod.POST, "/v1/databaseConfig/test").permitAll()
.antMatchers("/security/loginByPwd").permitAll()
.antMatchers("/security/loginByToken").permitAll()
.antMatchers("/security/vcode").permitAll();
//正式环境下所有的请求都要经过验证
if (envActive.equals("prod")) {
expressionInterceptUrlRegistry.accessDecisionManager(accessDecisionManager()).anyRequest().authenticated();
} else {
expressionInterceptUrlRegistry.anyRequest().permitAll();
}
// .permitAll() //权限允许所有人
// .and()
// .sessionManagement()
// .invalidSessionUrl("/login")
// .maximumSessions(5)
}
}
网友评论