Recientemente, encontré un problema en el proyecto: el front-end y el back-end están separados, el front-end se realiza con Vue, todas las solicitudes de datos usan Vue-Resource, y no se utiliza ningún formulario, por lo que JSON utiliza la interacción de datos, el fondo usa el arranque de la primavera y la verificación de permiso de permiso usa la seguridad de Spring. Debido a que la seguridad de primavera se usó antes, procesaron páginas, y esta vez simplemente procesaron las solicitudes de AJAX, por lo que registraron algunos problemas que encontraron. La solución aquí no solo es adecuada para solicitudes AJAX, sino que también resuelve la verificación de solicitudes móviles.
Crea un proyecto
En primer lugar, necesitamos crear un proyecto de arranque de primavera. Al crearlo, necesitamos presentar la seguridad web, Spring Security, MySQL y MyBatis (el marco de la base de datos es en realidad arbitraria, uso mybatis aquí). Después de la creación, el archivo de dependencia es el siguiente:
<Spendency> <MoupRoMID> org.mybatis.spring.boot </groupid> <artifactid> mybatis-spring-boot-starter </arfactid> <version> 1.3.1 </versión> </pendency> <epardency> <grupo> org.springframework.boot </proupid> <artifactid> spring-boot-starter-security </artifactid> </pendency> <pendency> <uproupid> org.springframework.boot </groupid> <artifactid> spring-boot-starter-web </artifactid> </dependency> <epardency> <uproupid> mysql </groupid> <artifactid> mysql-confonnector-java </arfactid> <ACPEPE> Runtime </cope> </pendency> <epardency> <proupid> commons-codec </groupid> <artifactid> commons-codec </artifactid> <versión> 1.11 </spersion> </dependencia>
Tenga en cuenta que la última dependencia de Commons-Codec fue agregada manualmente por mí. Este es un proyecto de código abierto de Apache que puede usarse para generar digestiones de mensajes MD5. Simplemente procesaré la contraseña en el siguiente texto.
Crear una base de datos y configurarla
Para simplificar la lógica, he creado tres tablas aquí, a saber, la tabla de usuario, la tabla de roles y la tabla de asociación de roles de usuario, de la siguiente manera:
A continuación, necesitamos hacer una configuración simple de nuestra base de datos en Application.Properties. Aquí estamos determinados por su situación específica.
spring.datasource.url = jdbc: mysql: ///vueblogspring.datasource.username=rootspring.datasource.password=123
Construir clase de entidad
Aquí se refiere principalmente a la construcción de clases de usuario. Las clases de usuarios aquí son bastante especiales y deben implementar la interfaz de dientes de usuario de usuario, de la siguiente manera:
El usuario de la clase pública implementa UserDetails {ID de largo privado; nombre de usuario de cadena privada; contraseña de cadena privada; apodo de cadena privada; booleano privado habilitado; roles de la lista privada <sol>; @Override public boolean isaccountnonexpired () {return true; } @Override public boolean isaccountnonlocked () {return true; } @Override public boolean isCredentialSnonExpired () {return true; } @Override public boolean iseNabled () {return habilitado; } @Override Public List <StorDauthority> getAuthorities () {list <ScededAuthority> autoridades = new ArrayList <> (); para (rol de rol: roles) {autorities.add (new SimpleGrantedAuthority ("rol_" + role.getName ())); } autoridades de retorno; } // getter/setter omit ...} Después de implementar la interfaz UserDetails, existen varios métodos en esta interfaz que necesitamos implementar. Los cuatro métodos que devuelven booleanos son todos conocidos y conocidos. Habilitado indica si la cuenta de período está habilitada. Este campo existe en mi base de datos. Por lo tanto, de acuerdo con los resultados de la consulta, el otro regresa directamente por períodos simples. El método GetAuthorities devuelve la información de roles del usuario actual. El papel del usuario es en realidad los datos en los roles. Los datos en los roles se convierten en List <StededAuthority> y luego se devuelven. Hay un punto a tener en cuenta aquí. Dado que los nombres de roles que almaceno en la base de datos son todos como 'Super Administrator', 'Usuario ordinario', etc., y no comienzan con personajes como ROLE_ , por lo que debe agregar manualmente ROLE_ aquí, recuerde.
También hay una clase de entidad de roles, que es relativamente simple y se puede crear de acuerdo con los campos de la base de datos. No lo repetiré aquí.
Crear usuario
El servicio de usuarios aquí también es bastante especial, y es necesario implementar la interfaz de Servicio de Servicio de Servicio de usuario, de la siguiente manera:
@ServicePublic UserService Implementa UserDetailsService {@aUtowired Usermapper Usermapper; @AutoWired RolesMapper RolesMapper; @Override Public UserDetails LoadUserByUserName (String S) lanza usernamenotfoundException {user user = usermapper.loadUserByUserName (s); if (user == null) {// Evite devolver nulo, aquí devuelve un objeto de usuario que no contiene ningún valor, y la verificación también fallará en el proceso de comparación de contraseña posterior return New User (); } // Consulta la información de rol del usuario y regresa al usuario. List <sol> roles = rolesmapper.getRolesbyUid (user.getID ()); user.setRoles (roles); devolver el usuario; }}Después de implementar la interfaz UserDetailsService, necesitamos implementar el método LoadUserByUserName en la interfaz, es decir, consulta al usuario según el nombre de usuario. Aquí se inyectan dos mapeadores en MyBatis, Usermapper se usa para consultar a los usuarios y RolesMapper se usa para consultar los roles. En el método LoadUserByUserName, primero consulte el usuario de acuerdo con los parámetros aprobados (el parámetro es el nombre de usuario ingresado cuando el usuario inicia sesión). Si el usuario encontrado es nulo, puede lanzar directamente una excepción de UserNaMenotFoundException. Sin embargo, para la comodidad del procesamiento, devolví un objeto de usuario sin ningún valor. De esta manera, durante el proceso de comparación de contraseña posterior, encontrará que el inicio de sesión falló (aquí puede ajustarlo de acuerdo con las necesidades de su negocio). Si el usuario encontrado no es nulo, consultaremos el papel del usuario en función de la ID de usuario y pondremos el resultado de la consulta en el objeto de usuario. Este resultado de la consulta se utilizará en el método GetAuthorities del objeto de usuario.
Configuración de seguridad
Primero echemos un vistazo a mi configuración de seguridad, y luego lo explicaré uno por uno:
@ConfigurationPublic Class WebSecurityConfig extiende WebSecurityConfigurerAdapter {@aUtoWired UserService UserService; @Override Protected void configure (autenticationManagerBuilder Auth) lanza la excepción {auth.userDetailsService (UserService) .PassWordEncoder (new PasswordEnCoder () {@Override public String codeod (CharShence CharSeSment) {returnUtils.md5DigEsTASHEX (charshent.ToStrence (). GetBytes () {returnUtils.md5digeShexx (charSeCeShence.ToStrence (). GetByTes ()*); @param CharSECHENCE ENTREMATORIO* @param s cifrado* @return*/ @Override public boolean coincidentes (CharSECHENCE CharSequence, String S) {return s.equals (digestUtils.md5DigESTASHEX (CharSECHENCE.ToString (). GetBytes ())); } @Override protegido void configure (httpsecurity http) lanza la excepción {http.authorizequests () .antmatchers ("/admin/**"). Hasrole ("Super Admin") .AnyRequest (). Authenticate () // Se acceden a otras rutas después de la registro in.and().formLogin().loginPage("/login_page").successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {httpservletResponse.setContentType (json; charset = utf-8 "); }) .FailureHandler (New AuthenticationFailureHandler () {@Override public void onAuthenticationFailure (httpservletRequest httpservletRequest, httpServletResponse httpServletReSponse, autenticationException e) lanzado ioexception, servletexception { httpservletResponse.setContentType }). LoginProcessingUrl ("/login") .UsernameParameter ("Nombre de usuario"). PasswordParameter ("Password"). Permitall () .and (). logrout (). Permitall (). y (). CSRF (). DIABLE (); } @Override public void configure (WebSecurity Web) lanza la excepción {web.ignoring (). Antmatchers ("/reg"); }}Este es el núcleo de nuestra configuración. Por favor, escúchame:
1. En primer lugar, esta es una clase de configuración, así que recuerde agregar la anotación @Configuration. Debido a que esta es la configuración de Spring Security, debe heredar el WebSecurityConfigurerAdapter.
2. Inyecte el servicio de usuarios que acaba de crear y lo usaremos más tarde.
3.Configure (AuthenticationManagerBuilder Auth) El método se utiliza para configurar nuestro método de autenticación y pasar UserService en el método Auth.userDetailsService (), de modo que el método LoadUserByusEname en el Método de UserService se llamará automáticamente cuando la contraseña de los usuarios. La contraseña de texto sin formato también es necesaria para procesarla al iniciar sesión, por lo que agregué contraseña, y después de agregar contraseñas en contraseña, puedo nuevo una clase interna anónima de contraseña. Hay dos métodos para implementar aquí, y puede conocer el significado del método mirando el nombre. El primer método codifica obviamente cifra el texto sin formato. Aquí uso el Digest del mensaje MD5. El método de implementación específico es proporcionado por la dependencia de Commons-Codec; La segunda coincidencia del método son la comparación de contraseñas, dos parámetros, el primer parámetro es la contraseña de texto sin formato y el segundo es el texto cifrado. Aquí solo necesita cifrar el texto sin formato y compararlo con el texto cifrado (si está interesado en esto, puede continuar considerando agregar sal a la contraseña).
4.Configure (httpsecurity http) se usa para configurar nuestras reglas de autenticación, etc. El método AuthorizeRequests significa que la configuración de la regla de autenticación está habilitada, AntMatchers ("/Admin/**"). Hasrole ("Super Administrador") significa que la ruta de /admin/** debe ser accedida por los usuarios con los usuarios con el "Super Administrador". Vi en Internet que mis amigos tienen preguntas sobre si agregar ROLE_ en el método Hasrole. No lo agregue aquí. Si usa el método Hasauthority, debe agregarlo. AnyRequest (). Authenticated () significa que todas las demás rutas deben ser autenticadas/iniciadas antes de acceder. A continuación, configuramos la página de inicio de sesión como login_page, la ruta de procesamiento de inicio de sesión es /inicio de sesión, el nombre de usuario de inicio de sesión es el nombre de usuario y la contraseña es contraseña. Configuramos estas rutas para acceder directamente, y la sesión de inicio de sesión también se puede acceder directamente y finalmente cerrar CSRF. En SuccessHandler, use la respuesta para devolver el JSON que ha iniciado sesión con éxito. Recuerde no usar el valor predeterminado. DefaultSuccessurl es una página que se redirige solo después de iniciar sesión con éxito. La misma razón también se usa para FailsHandler.
5. He configurado algunas reglas de filtrado en el método de configuración (websecurity web), por lo que no entraré en detalles.
6. Además, para archivos estáticos, como /images/** , /css/** y /js/** , el valor predeterminado no se intercepta.
Controlador
Finalmente, echemos un vistazo a nuestro controlador, como sigue:
@RestControllerPublic LoginregController {/** * Si salta automáticamente a esta página, significa que el usuario no está registrado y puede devolver el mensaje correspondiente * <p> * Si desea admitir el inicio de sesión de formulario, puede juzgar el tipo de solicitud en este método, y luego decidir si regresa a JSON o html Page * * @return */@RequestMapping ("/" "") LoginPage () {return New RespBean ("Error", "aún no inició sesión, ¡por favor inicie sesión!"); }} En general, este controlador es relativamente simple. Respira devuelve un JSON simple, que no es detallado. A lo que debe prestar atención aquí es login_page . La página de inicio de sesión que configuramos es un login_page . Pero de hecho, login_page no es una página, sino un JSON. Esto se debe a que cuando visito otras páginas sin iniciar sesión, Spring Security saltará automáticamente a la página login_page . Sin embargo, en solicitud de AJAX, este tipo de salto no es necesario. Todo lo que quiero es un aviso para iniciar sesión o no, para que pueda devolver JSON aquí.
prueba
Finalmente, los amigos pueden usar herramientas como Postman o RestClient para probar los problemas de inicio de sesión y permiso, por lo que no lo demostraré.
Ok, después de la introducción anterior, creo que los amigos ya han entendido el manejo de Spring Boot+Spring Security de las solicitudes de inicio de sesión de AJAX. Bien, eso es todo para este artículo. Si tiene alguna pregunta, deje un mensaje para discutir.