Spring Security 入门(3-11)Spring Security 的使用-自定义登录验证和回调地址

时间:2020-12-06 00:31:51
  1. 配置文件 security-ns.xml
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <beans xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  6. http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
  7. //需要过滤不被拦截的请求
  8. <security:http pattern="/openapi/**" security="none" />
  9. <security:http pattern="/useraccounts/userprofile.json" security="none" />
  10. <security:http pattern="/useraccounts/register**" security="none" />
  11. //entry-point-ref 配置自定义登录
  12. <security:http auto-config="false" entry-point-ref="authenticationEntryPoint">
  13. <security:intercept-url pattern="/backManage/**" access="ROLE_BACK_USER" />
  14. <security:intercept-url pattern="/mall/**"       access="ROLE_BACK_USER" />
  15. <security:intercept-url pattern="/thirdUser/**"  access="ROLE_USER" />
  16. <security:intercept-url pattern="/useraccounts/**" access="ROLE_USER" />
  17. <security:intercept-url pattern="/cart/**.html" access="ROLE_USER" />
  18. <security:intercept-url pattern="/ticket/**" access="ROLE_USER,ROLE_BACK_USER" />
  19. <security:intercept-url pattern="/order/**" access="ROLE_USER" />
  20. <security:intercept-url pattern="/comment/**" access="ROLE_USER" />
  21. <security:intercept-url pattern="/personal/**" access="ROLE_USER" />
  22. <security:intercept-url pattern="/favorite/**" access="ROLE_USER" />
  23. //需要替换的Filter顺序,配置自定义custom-filter时必须蔣auto-config="false",不然会报已经存在同样的过滤器错误
  24. <security:custom-filter ref="myLoginFilter"  position="FORM_LOGIN_FILTER" />
  25. //登出配置
  26. <security:logout logout-success-url="${local.service.url}"/>
  27. </security:http>
  28. //密码加密工具类
  29. <bean id="encoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/>
  30. //认证管理器
  31. <security:authentication-manager alias="authenticationManager">
  32. //UserDetailsService实现 主要用于用户的查询
  33. <security:authentication-provider user-service-ref="userLoginService">
  34. <security:password-encoder  ref="encoder">
  35. </security:password-encoder>
  36. </security:authentication-provider>
  37. </security:authentication-manager>
  38. <bean id="myLoginFilter" class="com.sale114.www.sercurity.MyUsernamePasswordAuthenticationFilter">
  39. <property name="authenticationManager" ref="authenticationManager"/>
  40. <property name="authenticationFailureHandler" ref="failureHandler"/>
  41. <property name="authenticationSuccessHandler" ref="successHandler"/>
  42. </bean>
  43. //成功登录后
  44. <bean id="successHandler" class="com.sale114.www.sercurity.MySavedRequestAwareAuthenticationSuccessHandler">
  45. <property name="defaultTargetUrl" value="${local.service.url}"/>
  46. </bean>
  47. //登录失败
  48. <bean id="failureHandler" class="com.sale114.www.sercurity.MySimpleUrlAuthenticationFailureHandler">
  49. <property name="defaultFailureUrl" value="${local.service.url}/login.html?validated=false"/>
  50. </bean>
  51. <bean id="authenticationEntryPoint"
  52. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
  53. <property name="loginFormUrl" value="${local.service.url}/login.html" />
  54. </bean>
  55. </beans>
  56. 2 UserLoginServiceImpl 查询用户实现类
  57. @Named("userLoginService")
  58. public class UserLoginServiceImpl  implements UserDetailsService ,LoginService{
  59. @Inject
  60. private UserLoginDAO userLoginDAO;
  61. @Override
  62. public WrappedUserLogin getUserLogin() {
  63. try {
  64. WrappedUserLogin wrappedUserLogin = (WrappedUserLogin) SecurityContextHolder
  65. .getContext().getAuthentication().getPrincipal();
  66. return wrappedUserLogin;
  67. } catch (Exception e) {
  68. return null;
  69. }
  70. }
  71. @Override
  72. public UserDetails loadUserByUsername(String username)
  73. throws UsernameNotFoundException {
  74. System.out.println("用户名-------------"+username);
  75. UserLogin userLogin =  null;
  76. if(username != null && !"".equals(username)&& username.indexOf("@") > 0){
  77. userLogin = userLoginDAO.findByEmail(username);
  78. username = userLogin.getNick();
  79. }else{
  80. userLogin = userLoginDAO.findByNick(username);
  81. }
  82. System.out.println("user is null ---"+userLogin.getUserType());
  83. String nick = userLogin.getNick();
  84. String email = userLogin.getEmail();
  85. String mobile = userLogin.getMobile();
  86. int userType = userLogin.getUserType();
  87. List<GrantedAuthority> resultAuths = new ArrayList<GrantedAuthority>();
  88. // 前台用户
  89. if (userType == 1) {
  90. resultAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
  91. } else {
  92. resultAuths.add(new SimpleGrantedAuthority("ROLE_BACK_USER"));
  93. }
  94. return new WrappedUserLogin(userLogin.getId(), email, nick, mobile, userLogin.getPassword(), userType,resultAuths);
  95. }
  96. }
  97. 3 重写用户名密码验证
  98. public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
  99. //用户名
  100. public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
  101. //密码
  102. public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
  103. //需要回调的URL 自定义参数
  104. public static final String SPRING_SECURITY_FORM_REDERICT_KEY = "spring-security-redirect";
  105. /**
  106. * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler}
  107. */
  108. @Deprecated
  109. public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
  110. private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
  111. private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
  112. private String redirectParameter = SPRING_SECURITY_FORM_REDERICT_KEY;
  113. private boolean postOnly = true;
  114. //~ Constructors ===================================================================================================
  115. public MyUsernamePasswordAuthenticationFilter() {
  116. super();
  117. }
  118. //~ Methods ========================================================================================================
  119. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
  120. if (postOnly && !request.getMethod().equals("POST")) {
  121. throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  122. }
  123. String username = obtainUsername(request);
  124. String password = obtainPassword(request);
  125. String redirectUrl = obtainRedercitUrl(request);
  126. if (username == null) {
  127. username = "";
  128. }
  129. if (password == null) {
  130. password = "";
  131. }
  132. //自定义回调URL,若存在则放入Session
  133. if(redirectUrl != null && !"".equals(redirectUrl)){
  134. request.getSession().setAttribute("callCustomRediretUrl", redirectUrl);
  135. }
  136. username = username.trim();
  137. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
  138. // Allow subclasses to set the "details" property
  139. setDetails(request, authRequest);
  140. return this.getAuthenticationManager().authenticate(authRequest);
  141. }
  142. /**
  143. * Enables subclasses to override the composition of the password, such as by including additional values
  144. * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
  145. * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
  146. * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
  147. *
  148. * @param request so that request attributes can be retrieved
  149. *
  150. * @return the password that will be presented in the <code>Authentication</code> request token to the
  151. *         <code>AuthenticationManager</code>
  152. */
  153. protected String obtainPassword(HttpServletRequest request) {
  154. return request.getParameter(passwordParameter);
  155. }
  156. /**
  157. * Enables subclasses to override the composition of the username, such as by including additional values
  158. * and a separator.
  159. *
  160. * @param request so that request attributes can be retrieved
  161. *
  162. * @return the username that will be presented in the <code>Authentication</code> request token to the
  163. *         <code>AuthenticationManager</code>
  164. */
  165. protected String obtainUsername(HttpServletRequest request) {
  166. return request.getParameter(usernameParameter);
  167. }
  168. protected String obtainRedercitUrl(HttpServletRequest request) {
  169. return request.getParameter(redirectParameter);
  170. }
  171. /**
  172. * Provided so that subclasses may configure what is put into the authentication request's details
  173. * property.
  174. *
  175. * @param request that an authentication request is being created for
  176. * @param authRequest the authentication request object that should have its details set
  177. */
  178. protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
  179. authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
  180. }
  181. /**
  182. * Sets the parameter name which will be used to obtain the username from the login request.
  183. *
  184. * @param usernameParameter the parameter name. Defaults to "j_username".
  185. */
  186. public void setUsernameParameter(String usernameParameter) {
  187. Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
  188. this.usernameParameter = usernameParameter;
  189. }
  190. /**
  191. * Sets the parameter name which will be used to obtain the password from the login request..
  192. *
  193. * @param passwordParameter the parameter name. Defaults to "j_password".
  194. */
  195. public void setPasswordParameter(String passwordParameter) {
  196. Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
  197. this.passwordParameter = passwordParameter;
  198. }
  199. /**
  200. * Defines whether only HTTP POST requests will be allowed by this filter.
  201. * If set to true, and an authentication request is received which is not a POST request, an exception will
  202. * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
  203. * will be called as if handling a failed authentication.
  204. * <p>
  205. * Defaults to <tt>true</tt> but may be overridden by subclasses.
  206. */
  207. public void setPostOnly(boolean postOnly) {
  208. this.postOnly = postOnly;
  209. }
  210. }
  211. 4 SimpleUrlAuthenticationSuccessHandler重写
  212. public class MySavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
  213. @Value(value = "${local.service.url}")
  214. private String LOCAL_SERVER_URL;
  215. protected final Log logger = LogFactory.getLog(this.getClass());
  216. private RequestCache requestCache = new HttpSessionRequestCache();
  217. @Override
  218. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
  219. Authentication authentication) throws ServletException, IOException {
  220. SavedRequest savedRequest = requestCache.getRequest(request, response);
  221. if (savedRequest == null) {
  222. System.out.println("savedRequest is null ");
  223. //用户判断是否要使用上次通过session里缓存的回调URL地址
  224. int flag = 0;
  225. //通过提交登录请求传递需要回调的URL callCustomRediretUrl
  226. if(request.getSession().getAttribute("callCustomRediretUrl") != null && !"".equals(request.getSession().getAttribute("callCustomRediretUrl"))){
  227. String url = String.valueOf(request.getSession().getAttribute("callCustomRediretUrl"));
  228. //若session 存在则需要使用自定义回调的URL 而不是缓存的URL
  229. super.setDefaultTargetUrl(url);
  230. super.setAlwaysUseDefaultTargetUrl(true);
  231. flag = 1;
  232. request.getSession().setAttribute("callCustomRediretUrl", "");
  233. }
  234. //重设置默认URL为主页地址
  235. if(flag  == 0){
  236. super.setDefaultTargetUrl(LOCAL_SERVER_URL);
  237. }
  238. super.onAuthenticationSuccess(request, response, authentication);
  239. return;
  240. }
  241. //targetUrlParameter 是否存在
  242. String targetUrlParameter = getTargetUrlParameter();
  243. if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
  244. requestCache.removeRequest(request, response);
  245. super.setAlwaysUseDefaultTargetUrl(false);
  246. super.setDefaultTargetUrl("/");
  247. super.onAuthenticationSuccess(request, response, authentication);
  248. return;
  249. }
  250. //清除属性
  251. clearAuthenticationAttributes(request);
  252. // Use the DefaultSavedRequest URL
  253. String targetUrl = savedRequest.getRedirectUrl();
  254. logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
  255. if(targetUrl != null && "".equals(targetUrl)){
  256. targetUrl = LOCAL_SERVER_URL;
  257. }
  258. getRedirectStrategy().sendRedirect(request, response, targetUrl);
  259. }
  260. public void setRequestCache(RequestCache requestCache) {
  261. this.requestCache = requestCache;
  262. }
  263. }
  264. 5 认证失败控制类重写
  265. /**
  266. * <tt>AuthenticationFailureHandler</tt> which performs a redirect to the value of the {@link #setDefaultFailureUrl
  267. * defaultFailureUrl} property when the <tt>onAuthenticationFailure</tt> method is called.
  268. * If the property has not been set it will send a 401 response to the client, with the error message from the
  269. * <tt>AuthenticationException</tt> which caused the failure.
  270. * <p>
  271. * If the {@code useForward} property is set, a {@code RequestDispatcher.forward} call will be made to
  272. * the destination instead of a redirect.
  273. *
  274. * @author Luke Taylor
  275. * @since 3.0
  276. */
  277. public class MySimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler{
  278. protected final Log logger = LogFactory.getLog(getClass());
  279. private String defaultFailureUrl;
  280. private boolean forwardToDestination = false;
  281. private boolean allowSessionCreation = true;
  282. private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
  283. @Value(value = "${local.service.url}")
  284. private String LOCAL_SERVER_URL;
  285. public MySimpleUrlAuthenticationFailureHandler() {
  286. }
  287. public MySimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
  288. setDefaultFailureUrl(defaultFailureUrl);
  289. }
  290. /**
  291. * Performs the redirect or forward to the {@code defaultFailureUrl} if set, otherwise returns a 401 error code.
  292. * <p>
  293. * If redirecting or forwarding, {@code saveException} will be called to cache the exception for use in
  294. * the target view.
  295. */
  296. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
  297. AuthenticationException exception) throws IOException, ServletException {
  298. //认证失败区别前后台:LOGIN URL
  299. if(request.getParameter("spring-security-redirect") != null){
  300. request.getSession().setAttribute("callUrlFailure", request.getParameter("spring-security-redirect"));
  301. }
  302. //若有loginUrl 则重定向到后台登录界面
  303. if(request.getParameter("loginUrl") != null && !"".equals(request.getParameter("loginUrl"))){
  304. defaultFailureUrl = LOCAL_SERVER_URL+"/backlogin.html?validated=false";
  305. }
  306. //defaultFailureUrl 默认的认证失败回调URL
  307. if (defaultFailureUrl == null) {
  308. logger.debug("No failure URL set, sending 401 Unauthorized error");
  309. response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage());
  310. } else {
  311. saveException(request, exception);
  312. if (forwardToDestination) {
  313. logger.debug("Forwarding to " + defaultFailureUrl);
  314. request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
  315. } else {
  316. logger.debug("Redirecting to " + defaultFailureUrl);
  317. redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
  318. }
  319. }
  320. }
  321. /**
  322. * Caches the {@code AuthenticationException} for use in view rendering.
  323. * <p>
  324. * If {@code forwardToDestination} is set to true, request scope will be used, otherwise it will attempt to store
  325. * the exception in the session. If there is no session and {@code allowSessionCreation} is {@code true} a session
  326. * will be created. Otherwise the exception will not be stored.
  327. */
  328. protected final void saveException(HttpServletRequest request, AuthenticationException exception) {
  329. if (forwardToDestination) {
  330. request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
  331. } else {
  332. HttpSession session = request.getSession(false);
  333. if (session != null || allowSessionCreation) {
  334. request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
  335. }
  336. }
  337. }
  338. /**
  339. * The URL which will be used as the failure destination.
  340. *
  341. * @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp".
  342. */
  343. public void setDefaultFailureUrl(String defaultFailureUrl) {
  344. this.defaultFailureUrl = defaultFailureUrl;
  345. }
  346. protected boolean isUseForward() {
  347. return forwardToDestination;
  348. }
  349. /**
  350. * If set to <tt>true</tt>, performs a forward to the failure destination URL instead of a redirect. Defaults to
  351. * <tt>false</tt>.
  352. */
  353. public void setUseForward(boolean forwardToDestination) {
  354. this.forwardToDestination = forwardToDestination;
  355. }
  356. /**
  357. * Allows overriding of the behaviour when redirecting to a target URL.
  358. */
  359. public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
  360. this.redirectStrategy = redirectStrategy;
  361. }
  362. protected RedirectStrategy getRedirectStrategy() {
  363. return redirectStrategy;
  364. }
  365. protected boolean isAllowSessionCreation() {
  366. return allowSessionCreation;
  367. }
  368. public void setAllowSessionCreation(boolean allowSessionCreation) {
  369. this.allowSessionCreation = allowSessionCreation;
  370. }
  371. }